Auth stack certs tab, arrs db fallbacks, cert monitor cache, conf parser fix
- Auth stack: fold cert monitor into Auth Stack page as fourth tab (Certs); remove standalone cert page and top-level tab - cert_monitor.sh: write JSON status cache to State_Files/cert_status.json after each run; expose per-domain days/expiry via _CERT_DAYS/_CERT_EXPIRY globals - api/cert.php: new — serves cached cert status; falls back to configured domains as UNKN when no cache exists; POST action=run triggers live check - arrs db fallbacks: vv_arr_cleanup_stats/discovery_stats/recovery_stats now read from data/*.db files when log JSON files don't yet exist - config.php vv_conf_vars(): unescape bash \$ → $ so passwords with dollar signs read correctly from conf files - host1.conf: fill in HOST1_NPM_USER/PASS and HOST1_LLDAP_USER/PASS - Partnership adapter pattern: Unraid-specific container logic extracted to Plugin/unraid/Partnership/; platform-agnostic structure stays in Partnership/ - First-run wizard: uniform multi-step flow for all hosts; HOST2 pull moved to checklist; auto SSH keygen and API key creation on save - api/checklist.php: live setup checklist with pull_master action - Fullscreen toggle: hide Unraid header/menu; state persists via localStorage
This commit is contained in:
@@ -20,7 +20,7 @@ $result = [
|
||||
];
|
||||
|
||||
// Show last debug log if present
|
||||
$debugFile = '/tmp/vv_api_debug.json';
|
||||
$debugFile = VV_CACHE_DIR . '/vv_api_debug.json';
|
||||
if (file_exists($debugFile)) {
|
||||
$result['debug_log'] = json_decode(file_get_contents($debugFile), true);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ if ($_action === 'refresh_remote') {
|
||||
if (!preg_match('/^host\d+$/', $host)) {
|
||||
echo json_encode(['ok' => false, 'error' => 'Invalid host']); exit;
|
||||
}
|
||||
$script = dirname(__DIR__) . '/tools/remote_arr_cache_writer.sh';
|
||||
$script = dirname(__DIR__) . '/Tools/remote_arr_cache_writer.sh';
|
||||
if (!file_exists($script)) {
|
||||
echo json_encode(['ok' => false, 'error' => 'remote_arr_cache_writer.sh not found']); exit;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
require_once dirname(__DIR__) . '/include/config.php';
|
||||
|
||||
$action = ($_SERVER['REQUEST_METHOD'] === 'POST')
|
||||
? trim($_POST['action'] ?? '')
|
||||
: trim($_GET['action'] ?? '');
|
||||
|
||||
$cacheFile = STATE_DIR . '/cert_status.json';
|
||||
|
||||
// ── Read configured domains (without running checks) ─────────────────────────
|
||||
if ($action === 'domains') {
|
||||
$hostId = vv_detect_host();
|
||||
$hostIdUp = strtoupper($hostId);
|
||||
$confRaw = ($hostId !== 'unknown') ? vv_read_conf_raw($hostId . '.conf') : '';
|
||||
$master = vv_read_conf_raw('master.conf');
|
||||
|
||||
// Extract CERT_WARN_DAYS / CERT_CRIT_DAYS from master
|
||||
preg_match('/^\s*CERT_WARN_DAYS\s*=\s*(\d+)/m', $master, $w);
|
||||
preg_match('/^\s*CERT_CRIT_DAYS\s*=\s*(\d+)/m', $master, $c);
|
||||
|
||||
// Extract domains array from host conf
|
||||
$domains = [];
|
||||
if (preg_match('/' . $hostIdUp . '_CERT_MONITOR_DOMAINS\s*=\s*\(([^)]*)\)/s', $confRaw, $dm)) {
|
||||
preg_match_all('/"([^"]+)"/', $dm[1], $dd);
|
||||
$domains = $dd[1] ?? [];
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'ok' => true,
|
||||
'host_id' => $hostId,
|
||||
'domains' => $domains,
|
||||
'warn_days' => (int)($w[1] ?? 30),
|
||||
'crit_days' => (int)($c[1] ?? 7),
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// ── Run cert_monitor.sh now ───────────────────────────────────────────────────
|
||||
if ($action === 'run') {
|
||||
$script = SCRIPTS_DIR . '/Monitors/cert_monitor.sh';
|
||||
if (!file_exists($script)) {
|
||||
echo json_encode(['ok' => false, 'error' => 'cert_monitor.sh not found']);
|
||||
exit;
|
||||
}
|
||||
set_time_limit(180);
|
||||
exec('bash ' . escapeshellarg($script) . ' 2>&1', $out, $rc);
|
||||
// Read freshly written cache
|
||||
$data = file_exists($cacheFile)
|
||||
? (json_decode(file_get_contents($cacheFile), true) ?: null)
|
||||
: null;
|
||||
echo json_encode([
|
||||
'ok' => true,
|
||||
'data' => $data,
|
||||
'output' => array_slice(array_filter(array_map('trim', $out)), 0, 30),
|
||||
'rc' => $rc,
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// ── Default: return cached status ─────────────────────────────────────────────
|
||||
if (!file_exists($cacheFile)) {
|
||||
// No cache yet — return configured domains so UI can show them unchecked
|
||||
$hostId = vv_detect_host();
|
||||
$hostIdUp = strtoupper($hostId);
|
||||
$confRaw = ($hostId !== 'unknown') ? vv_read_conf_raw($hostId . '.conf') : '';
|
||||
$master = vv_read_conf_raw('master.conf');
|
||||
|
||||
preg_match('/^\s*CERT_WARN_DAYS\s*=\s*(\d+)/m', $master, $w);
|
||||
preg_match('/^\s*CERT_CRIT_DAYS\s*=\s*(\d+)/m', $master, $c);
|
||||
$domains = [];
|
||||
if (preg_match('/' . $hostIdUp . '_CERT_MONITOR_DOMAINS\s*=\s*\(([^)]*)\)/s', $confRaw, $dm)) {
|
||||
preg_match_all('/"([^"]+)"/', $dm[1], $dd);
|
||||
foreach ($dd[1] ?? [] as $d) {
|
||||
$domains[] = ['domain' => $d, 'status' => 'UNKN', 'days' => null, 'expires' => ''];
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'ok' => true,
|
||||
'checked_at' => null,
|
||||
'host' => $hostId !== 'unknown' ? strtoupper($hostId) : null,
|
||||
'warn_days' => (int)($w[1] ?? 30),
|
||||
'crit_days' => (int)($c[1] ?? 7),
|
||||
'domains' => $domains,
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$data = json_decode(file_get_contents($cacheFile), true) ?: [];
|
||||
echo json_encode(array_merge(['ok' => true], $data));
|
||||
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
require_once dirname(__DIR__) . '/include/config.php';
|
||||
|
||||
$hostId = vv_detect_host();
|
||||
$hostIdUp = strtoupper($hostId);
|
||||
$master = vv_read_conf_raw('master.conf');
|
||||
$confRaw = ($hostId !== 'unknown') ? vv_read_conf_raw($hostId . '.conf') : '';
|
||||
|
||||
$items = [];
|
||||
|
||||
// ── Identity ──────────────────────────────────────────────────────────────────
|
||||
preg_match('/^\s*HOST1\s*=\s*"([^"]*)"/m', $master, $m1);
|
||||
$host1 = trim($m1[1] ?? '');
|
||||
$items[] = [
|
||||
'id' => 'identity',
|
||||
'label' => 'Server identity',
|
||||
'ok' => !empty($host1),
|
||||
'detail' => $host1 ? "HOST1: $host1" : 'HOST1 blank in master.conf',
|
||||
];
|
||||
|
||||
// ── Host conf ─────────────────────────────────────────────────────────────────
|
||||
$confExists = $hostId !== 'unknown' && file_exists(CONF_DIR . '/' . $hostId . '.conf');
|
||||
$items[] = [
|
||||
'id' => 'host_conf',
|
||||
'label' => 'Host configuration',
|
||||
'ok' => $confExists,
|
||||
'detail' => $confExists
|
||||
? "$hostId.conf present"
|
||||
: ($hostId === 'unknown' ? 'Server not yet identified' : "$hostId.conf missing"),
|
||||
];
|
||||
|
||||
// ── Unraid API key ─────────────────────────────────────────────────────────────
|
||||
$apiKey = trim(vv_parse_conf_scalar($confRaw, $hostIdUp . '_UNRAID_API_KEY'));
|
||||
$items[] = [
|
||||
'id' => 'api_key',
|
||||
'label' => 'Unraid API key',
|
||||
'ok' => !empty($apiKey),
|
||||
'detail' => $apiKey ? 'Key present' : 'Not set',
|
||||
'action' => $apiKey ? null : 'create_key',
|
||||
];
|
||||
|
||||
// ── SSH key ────────────────────────────────────────────────────────────────────
|
||||
$sshPath = trim(vv_parse_conf_scalar($confRaw, $hostIdUp . '_SSH_KEY'));
|
||||
$sshOk = $sshPath && file_exists($sshPath);
|
||||
$items[] = [
|
||||
'id' => 'ssh_key',
|
||||
'label' => 'SSH key',
|
||||
'ok' => $sshOk,
|
||||
'detail' => $sshOk
|
||||
? basename($sshPath)
|
||||
: ($sshPath ? "Path set but file missing: $sshPath" : 'No key path in host.conf'),
|
||||
'action' => $sshOk ? null : 'ssh_setup',
|
||||
];
|
||||
|
||||
// ── Auto-populate (any service key or container detected) ──────────────────────
|
||||
$populated = false;
|
||||
foreach (['_RADARR_API_KEY','_SONARR_API_KEY','_LIDARR_API_KEY','_EMBY_CONTAINER','_JELLYFIN_CONTAINER'] as $f) {
|
||||
if (trim(vv_parse_conf_scalar($confRaw, $hostIdUp . $f)) !== '') {
|
||||
$populated = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$items[] = [
|
||||
'id' => 'populated',
|
||||
'label' => 'Auto-populate',
|
||||
'ok' => $populated,
|
||||
'detail' => $populated ? 'Services detected in host.conf' : 'No services detected yet',
|
||||
'action' => $populated ? null : 'run_populate',
|
||||
];
|
||||
|
||||
// ── master.conf pull (partner servers only) ───────────────────────────────────────────────────
|
||||
if ($hostId !== 'host1' && $hostId !== 'unknown') {
|
||||
$state = vv_setup_state_read();
|
||||
$pulled = !empty($state['master_conf_pulled']);
|
||||
$items[] = [
|
||||
'id' => 'master_conf',
|
||||
'label' => 'master.conf',
|
||||
'ok' => $pulled,
|
||||
'detail' => $pulled
|
||||
? 'Synced from HOST1'
|
||||
: ($host1 ? "Not yet pulled from $host1" : 'HOST1 hostname not set in master.conf'),
|
||||
'action' => (!$pulled && $host1) ? 'pull_master' : null,
|
||||
];
|
||||
}
|
||||
|
||||
// ── Partnership (only if a partner is configured) ──────────────────────────────
|
||||
preg_match('/^\s*HOST2\s*=\s*"([^"]*)"/m', $master, $m2);
|
||||
$host2 = trim($m2[1] ?? '');
|
||||
if (!empty($host2)) {
|
||||
$state = vv_setup_state_read();
|
||||
$p1done = !empty($state['HOST2_PHASE1_DONE']) || !empty($state['host2_phase1_done']);
|
||||
$p2done = !empty($state['HOST2_PHASE2_DONE']) || !empty($state['host2_phase2_done']);
|
||||
$items[] = [
|
||||
'id' => 'partnership',
|
||||
'label' => 'Partnership',
|
||||
'ok' => $p1done && $p2done,
|
||||
'detail' => ($p1done && $p2done)
|
||||
? "Active with $host2"
|
||||
: ($p1done ? "Phase 1 done — waiting for HOST2 to complete" : "Not started — run partnership_onboard.sh"),
|
||||
'action' => (!$p1done) ? 'onboard' : null,
|
||||
];
|
||||
}
|
||||
|
||||
$allOk = !in_array(false, array_column($items, 'ok'), true);
|
||||
|
||||
echo json_encode(['ok' => true, 'complete' => $allOk, 'host_id' => $hostId, 'items' => $items]);
|
||||
@@ -9,76 +9,4 @@ if (!preg_match('/^host\d+$/', $host)) {
|
||||
exit;
|
||||
}
|
||||
|
||||
$hostUpper = strtoupper($host);
|
||||
$varName = $hostUpper . '_UNRAID_API_KEY';
|
||||
$confFile = $host . '.conf';
|
||||
|
||||
// Create/overwrite the Varaverk API key.
|
||||
// --description and --roles are required to suppress interactive prompts.
|
||||
// --overwrite replaces any existing key with the same name (keeps it to one).
|
||||
$dbg = ['ts' => date('H:i:s'), 'user' => trim(shell_exec('whoami'))];
|
||||
$output = shell_exec('timeout 10 /usr/local/sbin/unraid-api apikey --name "Varaverk" --create --overwrite --description "Varaverk plugin" --roles ADMIN --json </dev/null 2>&1');
|
||||
$dbg['raw'] = $output;
|
||||
file_put_contents('/tmp/vv_apikey_debug.json', json_encode($dbg, JSON_PRETTY_PRINT));
|
||||
|
||||
if (!$output) {
|
||||
echo json_encode(['ok' => false, 'error' => 'unraid-api returned no output — check /tmp/vv_apikey_debug.json']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$data = json_decode(trim($output), true);
|
||||
if (!is_array($data)) {
|
||||
echo json_encode(['ok' => false, 'error' => 'Could not parse unraid-api output', 'raw' => substr($output, 0, 300)]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$key = $data['key'] ?? null;
|
||||
if (!$key) {
|
||||
echo json_encode(['ok' => false, 'error' => 'No key in response', 'raw' => substr($output, 0, 300)]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Read conf, replace the key value, write back
|
||||
$raw = vv_read_conf_raw($confFile);
|
||||
if ($raw === '') {
|
||||
echo json_encode(['ok' => false, 'error' => 'Cannot read ' . $confFile]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// If line is missing (older conf created before this field was added to the template),
|
||||
// insert it after HOST*_OWNER_EMAIL, or after HOST*_SSH_KEY, or append to file.
|
||||
if (!str_contains($raw, $varName)) {
|
||||
$inserted = false;
|
||||
foreach ([$hostUpper . '_OWNER_EMAIL', $hostUpper . '_SSH_KEY'] as $anchor) {
|
||||
if (str_contains($raw, $anchor)) {
|
||||
$raw = preg_replace(
|
||||
'/^(\s*' . preg_quote($anchor, '/') . '\s*=.*$)/m',
|
||||
'$1' . "\n " . $varName . '=""',
|
||||
$raw, 1
|
||||
);
|
||||
$inserted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$inserted) {
|
||||
$raw = rtrim($raw) . "\n " . $varName . '=""' . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Replace quoted value in-place
|
||||
$updated = preg_replace(
|
||||
'/^(\s*' . preg_quote($varName, '/') . '\s*=\s*)"[^"]*"/m',
|
||||
'${1}"' . $key . '"',
|
||||
$raw
|
||||
);
|
||||
|
||||
if (!vv_write_conf_raw($confFile, $updated)) {
|
||||
echo json_encode(['ok' => false, 'error' => 'Failed to write ' . $confFile]);
|
||||
exit;
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'ok' => true,
|
||||
'key_preview' => substr($key, 0, 8) . '...' . substr($key, -4),
|
||||
'conf_file' => $confFile,
|
||||
]);
|
||||
echo json_encode(vv_auto_create_api_key($host, $host . '.conf'));
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
require_once dirname(__DIR__) . '/include/config.php';
|
||||
|
||||
$logDir = '/var/log/varaverk';
|
||||
$logDir = LOG_DIR;
|
||||
$runs = [];
|
||||
|
||||
try {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
require_once dirname(__DIR__) . '/include/config.php';
|
||||
|
||||
$scriptsDir = trim($_POST['scripts_dir'] ?? '');
|
||||
|
||||
@@ -13,7 +14,7 @@ if (!is_dir($scriptsDir)) {
|
||||
exit;
|
||||
}
|
||||
|
||||
$cfgFile = '/boot/config/plugins/varaverk/varaverk.cfg';
|
||||
$cfgFile = PLUGIN_CFG;
|
||||
$cfgDir = dirname($cfgFile);
|
||||
if (!is_dir($cfgDir)) mkdir($cfgDir, 0755, true);
|
||||
|
||||
|
||||
+110
-10
@@ -2,21 +2,89 @@
|
||||
header('Content-Type: application/json');
|
||||
require_once dirname(__DIR__) . '/include/config.php';
|
||||
|
||||
$action = ($_SERVER['REQUEST_METHOD'] === 'GET')
|
||||
? trim($_GET['action'] ?? '')
|
||||
: trim($_POST['action'] ?? 'save');
|
||||
|
||||
// ── GET: detect environment ────────────────────────────────────────────────────────────────────
|
||||
if ($action === 'detect') {
|
||||
$bootPart = trim(shell_exec('findmnt -n -o SOURCE /boot 2>/dev/null') ?: '');
|
||||
$bootDisk = $bootPart
|
||||
? trim(shell_exec('lsblk -no pkname ' . escapeshellarg($bootPart) . ' 2>/dev/null') ?: '')
|
||||
: '';
|
||||
$transport = $bootDisk
|
||||
? strtolower(trim(shell_exec('lsblk -dno TRAN /dev/' . escapeshellarg($bootDisk) . ' 2>/dev/null') ?: ''))
|
||||
: 'unknown';
|
||||
|
||||
$isUsb = ($transport === 'usb');
|
||||
|
||||
preg_match('/version="([^"]+)"/', @file_get_contents('/etc/unraid-version') ?: '', $vm);
|
||||
|
||||
echo json_encode([
|
||||
'ok' => true,
|
||||
'hostname' => vv_get_hostname(),
|
||||
'unraid_ver' => $vm[1] ?? 'unknown',
|
||||
'transport' => $transport,
|
||||
'boot_device' => $bootDisk ? '/dev/' . $bootDisk : 'unknown',
|
||||
'mode' => $isUsb ? 'flash' : 'internal',
|
||||
'scripts_dir' => SCRIPTS_DIR,
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// ── GET/POST: generate local SSH keypair ──────────────────────────────────────────────────────
|
||||
if ($action === 'ssh_generate') {
|
||||
$script = SCRIPTS_DIR . '/Partnership/ssh_setup.sh';
|
||||
if (!file_exists($script)) {
|
||||
echo json_encode(['ok' => false, 'error' => 'ssh_setup.sh not found']);
|
||||
exit;
|
||||
}
|
||||
exec('bash ' . escapeshellarg($script) . ' --local-only 2>&1', $out, $rc);
|
||||
// Derive pubkey path from hostname
|
||||
$hostname = vv_get_hostname();
|
||||
$shortName = strtolower(preg_replace('/^unraid-/i', '', $hostname));
|
||||
$pubPath = '/root/.ssh/' . $shortName . '_rsync_automation.pub';
|
||||
$pubKey = trim(@file_get_contents($pubPath) ?: '');
|
||||
echo json_encode([
|
||||
'ok' => $rc === 0 && !empty($pubKey),
|
||||
'pubkey' => $pubKey,
|
||||
'error' => ($rc !== 0) ? implode(' ', array_slice(array_filter(array_map('trim', $out)), -3)) : null,
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// ── POST: run conf_populate.sh ─────────────────────────────────────────────────────────────────
|
||||
if ($action === 'populate') {
|
||||
$script = SCRIPTS_DIR . '/Plugin/unraid/Tools/conf_populate.sh';
|
||||
if (!file_exists($script)) {
|
||||
echo json_encode(['ok' => false, 'error' => 'conf_populate.sh not found']);
|
||||
exit;
|
||||
}
|
||||
exec('bash ' . escapeshellarg($script) . ' --no-push 2>&1', $out, $rc);
|
||||
$lines = array_values(array_filter(array_map('trim', $out)));
|
||||
echo json_encode(['ok' => $rc === 0, 'lines' => array_slice($lines, 0, 20)]);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
echo json_encode(['ok' => false, 'error' => 'Method not allowed']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$action = trim($_POST['action'] ?? 'save');
|
||||
$sshScript = SCRIPTS_DIR . '/Partnership/ssh_setup.sh';
|
||||
|
||||
// ── HOST2 pull: pull master.conf from HOST1 via SSH ──────────────────────────────────────────
|
||||
// ── Pull master.conf from HOST1 via SSH (wizard or checklist) ────────────────────────────────
|
||||
if ($action === 'pull') {
|
||||
$host1Hostname = trim($_POST['host1_hostname'] ?? '');
|
||||
$mySlot = trim($_POST['my_slot'] ?? 'host2');
|
||||
$myHostname = trim($_POST['my_hostname'] ?? '');
|
||||
|
||||
$mySlot = trim($_POST['my_slot'] ?? '') ?: strtolower(vv_detect_host());
|
||||
$myHostname = trim($_POST['my_hostname'] ?? '') ?: vv_get_hostname();
|
||||
$host1Hostname = trim($_POST['host1_hostname'] ?? '');
|
||||
if (!$host1Hostname) {
|
||||
echo json_encode(['ok' => false, 'error' => 'HOST1 hostname required']);
|
||||
$masterRaw = vv_read_conf_raw('master.conf');
|
||||
preg_match('/^\s*HOST1\s*=\s*"([^"]*)"/m', $masterRaw, $_mh);
|
||||
$host1Hostname = trim($_mh[1] ?? '');
|
||||
}
|
||||
if (!$host1Hostname) {
|
||||
echo json_encode(['ok' => false, 'error' => 'HOST1 hostname not set — fill in master.conf first']);
|
||||
exit;
|
||||
}
|
||||
if (!preg_match('/^host\d+$/', $mySlot)) {
|
||||
@@ -71,17 +139,31 @@ if ($action === 'pull') {
|
||||
if (!file_exists(CONF_DIR . '/' . $confFile)) {
|
||||
$template = @file_get_contents(CONF_DIR . '/host.conf.template') ?: '';
|
||||
if ($template) {
|
||||
$hostname = $myHostname ?: vv_get_hostname();
|
||||
$sshKeyPath = $sshKey;
|
||||
$bootPart2 = trim(shell_exec('findmnt -n -o SOURCE /boot 2>/dev/null') ?: '');
|
||||
$bootDisk2 = $bootPart2 ? trim(shell_exec('lsblk -no pkname ' . escapeshellarg($bootPart2) . ' 2>/dev/null') ?: '') : '';
|
||||
$transport2 = $bootDisk2 ? strtolower(trim(shell_exec('lsblk -dno TRAN /dev/' . escapeshellarg($bootDisk2) . ' 2>/dev/null') ?: '')) : '';
|
||||
$storageInternal2 = ($transport2 !== 'usb') ? 'true' : 'false';
|
||||
$conf = str_replace('HOSTN', $hostId, $template);
|
||||
$conf = str_replace('hostn', $hostIdLow, $conf);
|
||||
$conf = preg_replace('/^(\s*' . $hostId . '_SSH_KEY\s*=\s*)""/m',
|
||||
'${1}"' . $sshKeyPath . '"', $conf);
|
||||
'${1}"' . $sshKey . '"', $conf);
|
||||
$conf = preg_replace('/^(\s*' . $hostId . '_STORAGE_MODE_INTERNAL\s*=\s*)\S+/m',
|
||||
'${1}' . $storageInternal2, $conf);
|
||||
vv_write_conf_raw($confFile, $conf);
|
||||
}
|
||||
}
|
||||
|
||||
if (file_exists($sshScript)) {
|
||||
exec('bash ' . escapeshellarg($sshScript) . ' --local-only 2>/dev/null');
|
||||
}
|
||||
$apiKeyResult = vv_auto_create_api_key($hostId, $confFile);
|
||||
|
||||
$state = vv_setup_state_read();
|
||||
$state['master_conf_pulled'] = 'true';
|
||||
vv_setup_state_write($state);
|
||||
|
||||
echo json_encode(['ok' => true, 'host_id' => $hostId, 'conf_file' => $confFile,
|
||||
'api_key' => $apiKeyResult,
|
||||
'redirect' => '?tab=scheduler&vv_setup=' . $confFile]);
|
||||
exit;
|
||||
}
|
||||
@@ -138,10 +220,19 @@ if (!file_exists(CONF_DIR . '/' . $confFile)) {
|
||||
if ($template) {
|
||||
$sshOwner = strtolower(preg_replace('/^unraid-/i', '', $myHostname));
|
||||
$sshKeyPath = '/root/.ssh/' . $sshOwner . '_rsync_automation';
|
||||
|
||||
// Auto-detect storage mode from boot device transport
|
||||
$bootPart = trim(shell_exec('findmnt -n -o SOURCE /boot 2>/dev/null') ?: '');
|
||||
$bootDisk = $bootPart ? trim(shell_exec('lsblk -no pkname ' . escapeshellarg($bootPart) . ' 2>/dev/null') ?: '') : '';
|
||||
$transport = $bootDisk ? strtolower(trim(shell_exec('lsblk -dno TRAN /dev/' . escapeshellarg($bootDisk) . ' 2>/dev/null') ?: '')) : '';
|
||||
$storageInternal = ($transport !== 'usb') ? 'true' : 'false';
|
||||
|
||||
$conf = str_replace('HOSTN', $hostId, $template);
|
||||
$conf = str_replace('hostn', $hostIdLow, $conf);
|
||||
$conf = preg_replace('/^(\s*' . $hostId . '_SSH_KEY\s*=\s*)""/m',
|
||||
'${1}"' . $sshKeyPath . '"', $conf);
|
||||
$conf = preg_replace('/^(\s*' . $hostId . '_STORAGE_MODE_INTERNAL\s*=\s*)\S+/m',
|
||||
'${1}' . $storageInternal, $conf);
|
||||
if (!vv_write_conf_raw($confFile, $conf)) {
|
||||
echo json_encode(['ok' => false, 'error' => "Failed to write $confFile"]);
|
||||
exit;
|
||||
@@ -152,8 +243,17 @@ if (!file_exists(CONF_DIR . '/' . $confFile)) {
|
||||
// Write setup state file — lets partner servers know HOST1 is configured
|
||||
vv_setup_state_write(['host1_hostname' => $host1]);
|
||||
|
||||
// Auto-generate SSH keypair (local only — remote copy happens during onboarding)
|
||||
if (file_exists($sshScript)) {
|
||||
exec('bash ' . escapeshellarg($sshScript) . ' --local-only 2>/dev/null');
|
||||
}
|
||||
|
||||
// Auto-create Unraid API key and write into the fresh conf
|
||||
$apiKeyResult = vv_auto_create_api_key($hostId, $confFile);
|
||||
|
||||
echo json_encode([
|
||||
'ok' => true,
|
||||
'host_id' => $hostId,
|
||||
'api_key' => $apiKeyResult,
|
||||
'redirect' => '?tab=scheduler&vv_setup=master.conf',
|
||||
]);
|
||||
|
||||
@@ -56,7 +56,7 @@ if ($action === 'migrate' && $_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
exit;
|
||||
}
|
||||
|
||||
$script = dirname(__DIR__) . '/tools/storage_migrate.sh';
|
||||
$script = dirname(__DIR__) . '/Tools/storage_migrate.sh';
|
||||
if (!file_exists($script)) {
|
||||
echo json_encode(['ok' => false, 'error' => 'storage_migrate.sh not found']);
|
||||
exit;
|
||||
@@ -141,7 +141,7 @@ if ($action === 'api_status') {
|
||||
|
||||
// ── Setup/renew API keys (local + all partners via SSH) ───────────────────────
|
||||
if ($action === 'setup_apikeys' && $_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$script = SCRIPTS_DIR . '/System_Essentials/unraid_api_key_renew.sh';
|
||||
$script = SCRIPTS_DIR . '/Plugin/unraid/System_Essentials/unraid_api_key_renew.sh';
|
||||
if (!file_exists($script)) {
|
||||
echo json_encode(['ok' => false, 'error' => 'unraid_api_key_renew.sh not found']); exit;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ $cmd = match($action) {
|
||||
};
|
||||
|
||||
$logLine = date('Y-m-d H:i:s') . " action={$action} ip=" . ($_SERVER['REMOTE_ADDR'] ?? 'unknown') . "\n";
|
||||
@file_put_contents('/boot/config/plugins/varaverk/actions.log', $logLine, FILE_APPEND | LOCK_EX);
|
||||
@file_put_contents(SCRIPTS_DIR . '/actions.log', $logLine, FILE_APPEND | LOCK_EX);
|
||||
|
||||
exec($cmd . ' > /dev/null 2>&1 &');
|
||||
echo json_encode(['ok' => true]);
|
||||
|
||||
Reference in New Issue
Block a user