Add Rsync page, upgrade monitor rsync card, partnership overhaul, API key periodic check, docker watchdog manual-stop detection
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
header('Cache-Control: no-store, no-cache');
|
||||
require_once dirname(__DIR__) . '/include/config.php';
|
||||
require_once dirname(__DIR__) . '/include/confform.php';
|
||||
|
||||
// All PROFILE_* assoc arrays and their UI field keys
|
||||
const RP_ARRAYS = [
|
||||
'PROFILE_RSYNC_OPTS' => 'rsync_opts',
|
||||
'PROFILE_BW_LIMIT' => 'bw_limit',
|
||||
'PROFILE_RETRY_COUNT' => 'retry_count',
|
||||
'PROFILE_SLEEP' => 'sleep',
|
||||
'PROFILE_CRITICAL_CONTAINER_NAMES' => 'critical_containers',
|
||||
'PROFILE_DELAYED_CONTAINERS' => 'delayed_containers',
|
||||
'PROFILE_CONTAINER_DELAY' => 'container_delay',
|
||||
'PROFILE_EXCLUDE_DIRS' => 'exclude_dirs',
|
||||
'PROFILE_REMOTE_RESTART_CONTAINERS' => 'remote_restart',
|
||||
];
|
||||
|
||||
// Parse [key]="value" or [key]=bare entries from an assoc_array body
|
||||
function _rp_parse_assoc(string $body): array {
|
||||
$result = [];
|
||||
preg_match_all('/\[([^\]]+)\]\s*=\s*(?:"([^"]*)"|([^\s#\n]*))/', $body, $m, PREG_SET_ORDER);
|
||||
foreach ($m as $match) {
|
||||
$key = $match[1];
|
||||
$val = $match[2] !== '' ? $match[2] : ($match[3] ?? '');
|
||||
$result[$key] = $val;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
// Rebuild assoc_array body from entries map
|
||||
function _rp_build_assoc(array $entries): string {
|
||||
$lines = [];
|
||||
foreach ($entries as $k => $v) {
|
||||
// Quote if empty, has spaces or special shell chars
|
||||
if ($v === '' || preg_match('/[\s\$\!\[\]\(\)\|\'`\\\\]/', $v)) {
|
||||
$lines[] = ' [' . $k . ']="' . str_replace('"', '\\"', $v) . '"';
|
||||
} else {
|
||||
$lines[] = ' [' . $k . ']=' . $v;
|
||||
}
|
||||
}
|
||||
return implode("\n", $lines);
|
||||
}
|
||||
|
||||
// Read all PROFILE_* arrays from master.conf → [profile_name => [field => value]]
|
||||
function _rp_read_all(): array {
|
||||
$raw = vv_read_conf_raw('master.conf');
|
||||
$result = [];
|
||||
foreach (RP_ARRAYS as $varName => $fieldKey) {
|
||||
if (!preg_match('/declare\s+-A\s+' . preg_quote($varName, '/') . '\s*=\s*\((.*?)\)/s', $raw, $m)) continue;
|
||||
foreach (_rp_parse_assoc($m[1]) as $profile => $value) {
|
||||
$result[$profile][$fieldKey] = $value;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
$action = trim($_GET['action'] ?? $_POST['action'] ?? '');
|
||||
|
||||
// ── list ──────────────────────────────────────────────────────────────────────
|
||||
if ($action === 'list') {
|
||||
echo json_encode(['ok' => true, 'profiles' => _rp_read_all()]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// ── save (create or update) ───────────────────────────────────────────────────
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && $action === 'save') {
|
||||
$name = trim($_POST['name'] ?? '');
|
||||
if (!$name || !preg_match('/^[a-zA-Z0-9_\-]+$/', $name)) {
|
||||
echo json_encode(['ok' => false, 'error' => 'Invalid profile name — use letters, numbers, hyphens, underscores']); exit;
|
||||
}
|
||||
|
||||
$raw = vv_read_conf_raw('master.conf');
|
||||
$changes = [];
|
||||
|
||||
foreach (RP_ARRAYS as $varName => $fieldKey) {
|
||||
$value = trim($_POST[$fieldKey] ?? '');
|
||||
// Find and parse current array body
|
||||
if (!preg_match('/declare\s+-A\s+' . preg_quote($varName, '/') . '\s*=\s*\((.*?)\)/s', $raw, $m)) continue;
|
||||
$entries = _rp_parse_assoc($m[1]);
|
||||
$entries[$name] = $value;
|
||||
$changes[] = [
|
||||
'file' => 'master.conf',
|
||||
'key' => $varName,
|
||||
'type' => 'assoc_array',
|
||||
'value' => _rp_build_assoc($entries),
|
||||
];
|
||||
}
|
||||
|
||||
if (!$changes) { echo json_encode(['ok' => false, 'error' => 'No profile arrays found in master.conf']); exit; }
|
||||
|
||||
$results = vv_conf_write_changes($changes);
|
||||
$ok = !in_array(false, $results, true);
|
||||
if ($ok) { vv_push_master_conf(); vv_push_setup_state(); }
|
||||
echo json_encode(['ok' => $ok, 'results' => $results]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// ── delete ────────────────────────────────────────────────────────────────────
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && $action === 'delete') {
|
||||
$name = trim($_POST['name'] ?? '');
|
||||
if (!$name || !preg_match('/^[a-zA-Z0-9_\-]+$/', $name)) {
|
||||
echo json_encode(['ok' => false, 'error' => 'Invalid profile name']); exit;
|
||||
}
|
||||
|
||||
$raw = vv_read_conf_raw('master.conf');
|
||||
$changes = [];
|
||||
|
||||
foreach (RP_ARRAYS as $varName => $fieldKey) {
|
||||
if (!preg_match('/declare\s+-A\s+' . preg_quote($varName, '/') . '\s*=\s*\((.*?)\)/s', $raw, $m)) continue;
|
||||
$entries = _rp_parse_assoc($m[1]);
|
||||
if (!array_key_exists($name, $entries)) continue;
|
||||
unset($entries[$name]);
|
||||
$changes[] = [
|
||||
'file' => 'master.conf',
|
||||
'key' => $varName,
|
||||
'type' => 'assoc_array',
|
||||
'value' => _rp_build_assoc($entries),
|
||||
];
|
||||
}
|
||||
|
||||
if (!$changes) { echo json_encode(['ok' => false, 'error' => 'Profile not found']); exit; }
|
||||
|
||||
$results = vv_conf_write_changes($changes);
|
||||
$ok = !in_array(false, $results, true);
|
||||
if ($ok) { vv_push_master_conf(); vv_push_setup_state(); }
|
||||
echo json_encode(['ok' => $ok]);
|
||||
exit;
|
||||
}
|
||||
|
||||
echo json_encode(['ok' => false, 'error' => 'Unknown action']);
|
||||
Reference in New Issue
Block a user