From d5c4d93b54301104eb0a44d011637ae0ac4d2de9 Mon Sep 17 00:00:00 2001 From: Alexander Schiemann Date: Tue, 20 May 2025 16:24:04 +0200 Subject: [PATCH] Day Commit --- index.php | 54 ++++++++++++---------- update.php | 8 +--- updater.php | 128 +++++++++++++++++++++++++++++++++++++--------------- 3 files changed, 124 insertions(+), 66 deletions(-) diff --git a/index.php b/index.php index b820175..58e976d 100644 --- a/index.php +++ b/index.php @@ -112,21 +112,18 @@ function error_deny() : void { } /** - * Retrieves a parameter from the request (POST takes precedence over GET). - * If the parameter is not found in either, returns the provided default value. + * Retrieves a request parameter from POST or GET with fallback to default. * - * @param string $name The name of the parameter to retrieve - * @param mixed $def The default value to return if parameter is not found - * @return mixed The value of the parameter or the default value + * @param string $name The name of the parameter to retrieve. + * @param mixed $def The default value if the parameter is not found or empty. + * @return mixed The parameter value or the default. */ function getQuery(string $name, mixed $def): mixed { - $val = $_POST[$name] ?? null; + // Check POST first, then GET, fallback to default + $val = $_POST[$name] ?? $_GET[$name] ?? $def; - if ($val === null) { - $val = $_GET[$name] ?? $def; - } - - return $val; + // Return default if value is empty (including trimmed empty strings) + return isEmpty($val) ? $def : $val; } /** @@ -196,14 +193,6 @@ function loadJson(string $filename): array { * @return bool True if saved successfully, false otherwise */ function saveJson(string $filename, ?array $val): bool { - // If file exists, check for write permissions - if (file_exists($filename)) { - if (!is_writable($filename)) { - echo "No access to write in $filename\n"; - return false; - } - } - // Default to empty array if null or empty if (isEmpty($val)) { $val = []; @@ -216,13 +205,30 @@ function saveJson(string $filename, ?array $val): bool { return false; } - // Save to file - if (file_put_contents($filename, $encoded) === false) { - echo "Failed to write to $filename\n"; - return false; + // If file exists, check for write permissions + if (file_exists($filename)) { + if (!is_writable($filename)) { + echo "No access to write in $filename\n"; + return false; + } + } +-+ + // Try writing up to 10 times with 250ms delay + $success = false; + $attempt = 0; + for ($attempt = 1; $attempt <= 10; $attempt++) { + if (@file_put_contents($filename, $encoded) !== false) { + $success = true; + break; + } + usleep(250 * 1000); // 250 ms delay } - return true; + if (!$success) { + echo "Failed to write to $filename after $attempt attempts\n"; + } + + return $success; } diff --git a/update.php b/update.php index f02d63c..038394c 100644 --- a/update.php +++ b/update.php @@ -26,9 +26,9 @@ function main(): void { // Retrieve parameters from GET or POST $name = getQuery('name', null); - $domain = getQuery('domain', null); - $token = getQuery('token', null); + $domain = getQuery('domain', $clientIp); $ip = getQuery('ip', $clientIp); + $token = getQuery('token', null); $port = getQuery('port', 80); $protocol = getQuery('protocol', 'http'); @@ -58,10 +58,6 @@ function main(): void { error_deny(); } - if ($domain === null || $domain === '') { - $domain = $ip; - } - // Update metadata and log the change updateMeta($name, $domain, $ip, $port, $protocol); } diff --git a/updater.php b/updater.php index 02b8f29..6dc8be2 100644 --- a/updater.php +++ b/updater.php @@ -39,15 +39,12 @@ function main(): void { // Loop through all domains and apply updates if 'changed' flag is set $changed = false; foreach ($domains as $name => $meta) { - if (!isset($meta['changed']) || !$meta['changed']) { - continue; - } - // Process and detect if actual change occurred - $changed |= processDomain($name, $meta); - - // Reset 'changed' flag after processing - $domains[$name]['changed'] = 0; + if (processDomain($name, $meta)) { + // Reset 'changed' flag after processing + $domains[$name]['changed'] = 0; + $changed = true; + } } // Restart nginx if any updates were made and save new state @@ -64,43 +61,104 @@ function main(): void { echo "All applicable domains processed.\n"; } + /** - * Modify the proxy_pass value inside a specific location block + * Generic processor for a location block in NGINX config lines. + * Applies a callback to each line inside the matched location block. + * + * @param array $lines NGINX config lines + * @param string $path Location path to match (e.g. "/api/", "\.php$") + * @param string|null $modifier Location modifier (e.g. "~", "^~", "=", or null) + * @param callable $callback Callback: function(array &$lines, int $i): void + * @return array Modified lines */ -function updateProxyPassInLocation(string $nginxConf, string $locationPath, string $newProxyPass): string { - $lines = explode("\n", $nginxConf); // Split config into lines - $inBlock = false; // Flag: inside target location block - $locationStart = false; - $braceLevel = 0; // Tracks nested braces +function processLocationBlock(array $lines, string $path, ?string $modifier, callable $callback): array { + $inBlock = false; + $braceLevel = 0; + $escapedPath = preg_quote($path, '/'); + + if ($modifier !== null) { + $pattern = "/^\\s*location\\s+{$escapedPath}\\s+$modifier\\s*\\{/"; // e.g. location /api/ ^~ { + } else { + $pattern = "/^\\s*location\\s+{$escapedPath}\\s*\\{/"; // e.g. location /api/ { + } foreach ($lines as $i => $line) { - $escapedLocation = preg_quote($locationPath, '/'); // Escape location for regex - if (preg_match("/^\s*location\s+$escapedLocation\s*\{\s*$/", $line)) { - $inBlock = true; - $locationStart = true; - $braceLevel = 1; + if (!$inBlock && preg_match($pattern, $line)) { + $inBlock = true; + $braceLevel = substr_count($line, '{') - substr_count($line, '}'); + $callback($lines, $i, 'start'); continue; } if ($inBlock) { - if (strpos($line, '{') !== false) $braceLevel++; - if (strpos($line, '}') !== false) $braceLevel--; + $braceLevel += substr_count($line, '{'); + $braceLevel -= substr_count($line, '}'); - // Replace existing proxy_pass directive - if (preg_match("/proxy_pass\\s+.+;/", $line)) { - $lines[$i] = preg_replace("/proxy_pass\\s+.+;/", "proxy_pass $newProxyPass;", $line); - } + $callback($lines, $i, 'inside'); - // Exit block if all braces are closed - if ($braceLevel === 0) { + if ($braceLevel <= 0) { + $callback($lines, $i, 'end'); $inBlock = false; } } } - return implode("\n", $lines); // Return modified config + return $lines; } + +function removeLocationBlock(array &$lines, string $path, ?string $modifier = null): array { + $result = []; + $skipBlock = false; + $buffer = []; + + $lines = processLocationBlock($lines, $path, $modifier, function(array &$lines, int $i, string $phase) use (&$skipBlock, &$buffer) { + if ($phase === 'start') { + $skipBlock = true; + } + if ($skipBlock) { + $lines[$i] = null; // Mark for removal + } + if ($phase === 'end') { + $skipBlock = false; + } + }); + + // Filter out null lines + foreach ($lines as $line) { + if (!isEmpty($line)) $result[] = $line; + } + + return $result; +} + +/** + * Updates one or more directives (e.g. proxy_pass) inside a given location block. + * + * @param array $lines Array of nginx configuration lines. + * @param string $locationPath The location path to match (e.g. "/api/"). + * @param string|null $modifier Optional modifier (e.g. "~", "^~"). + * @param array $replacements List of replacements: [['name' => 'proxy_pass', 'val' => 'http://...'], ...] + * @return array Modified nginx config lines. + */ +function updateLocationBlock(array $lines, string $locationPath, ?string $modifier, array $replacements): array { + return processLocationBlock($lines, $locationPath, $modifier, function(array &$lines, int $i, string $phase) use ($replacements) { + if ($phase === 'inside') { + foreach ($replacements as $rep) { + if (!isset($rep['name'], $rep['val'])) continue; + + $name = preg_quote($rep['name'], '/'); + $val = $rep['val']; + if (preg_match("/$name\\s+.+;/", $lines[$i])) { + $lines[$i] = preg_replace("/$name\\s+.+;/", "$name $val;", $lines[$i]); + } + } + } + }); +} + + /** * Processes a single domain configuration from meta.json. * @@ -135,10 +193,6 @@ function processDomain(string $name, array $meta): bool { // Allow forced reloads even without content change by setting "changed": 1 in meta.json $result = boolval($meta['changed'] ?? 1); - if (!$result) { - echo "No changes for $name\n"; - return false; - } // Full path to nginx config file for this domain $systemConfPath = VHOST_BASE . '/' . $name . '/conf/' . NGINX_CONF_NAME; @@ -155,19 +209,21 @@ function processDomain(string $name, array $meta): bool { // Read current nginx config $original = file_get_contents($systemConfPath); + $lines = explode("\n", $original); // Split config into lines // Build new proxy_pass target $proxyTarget = $meta['protocol'] . '://' . $meta['domain'] . ':' . $meta['port']; // Apply transformation inside matching location block - $modified = updateProxyPassInLocation($original, $meta['location'], $proxyTarget); + $lines = updateLocationBlock($lines, $meta['location'], null, [['name' => 'proxy_pass', 'val' => $proxyTarget]]); + // remove from nginx php обработчик так как он приводит к обработке локально, а не на удаленном сервере + $lines = removeLocationBlock($lines, '~', '.*\.php.*'); + $modified = implode("\n", $lines); // Write only if content changed if ($original !== $modified) { $result = (file_put_contents($systemConfPath, $modified) !== false); - echo "Updated proxy_pass for $name\n"; - } else { - echo "No changes for $name\n"; + echo "Domain: $name is updated\n"; } return $result;