From a9aea99028147a033c33909711ba4abe88d1ae98 Mon Sep 17 00:00:00 2001 From: Maaxxs <61059039+MaaxxsDev@users.noreply.github.com> Date: Tue, 21 Jul 2026 00:54:34 +0200 Subject: [PATCH] Fix data export/import: PHP's default upload limits rejected real exports Root cause: the export embeds the whole gallery as base64 directly in the JSON (that's how it stays a single self-contained file), which pushes a realistic export well past 40MB. PHP's post_max_size/upload_max_filesize default to values well under that on most hosting, so the browser's upload got silently discarded before the app ever saw it - PHP clears $_FILES and $_POST once post_max_size is exceeded, and the leftover raw body in php://input is unparsed multipart data, not JSON, so it fell into a generic "invalid file" 422 with no indication of what actually went wrong. Reproduced locally with a real ~43MB export against the previous 40M limit. Raises the limits to 200M via two paths, since we don't know which PHP SAPI the various hosting targets (All-Inkl, the Plesk test server) use: hifi/.htaccess sets php_value overrides for classic Apache module PHP, guarded by checks for several common module names so hosts running PHP-FPM/CGI (which ignore php_value and would otherwise choke on an unrecognized directive) skip the block instead of 500ing the entire site; hifi/api/public/.user.ini covers exactly that FPM/CGI case, which mod_php hosts in turn simply don't read. Also makes SettingsController::importData() detect an oversized upload by comparing Content-Length against the configured post_max_size, and report the actual limit instead of the generic corrupt-file message - so if some host's real limit is still too low, the admin sees why instead of a dead end. Co-Authored-By: Claude Fable 5 --- hifi/.htaccess | 28 ++++++++++++ hifi/api/public/.user.ini | 8 ++++ .../src/Controllers/SettingsController.php | 44 ++++++++++++++++++- 3 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 hifi/api/public/.user.ini diff --git a/hifi/.htaccess b/hifi/.htaccess index 52d6f9c..f7802f0 100644 --- a/hifi/.htaccess +++ b/hifi/.htaccess @@ -22,6 +22,34 @@ RewriteBase / # Upload mit "Methode nicht erlaubt" fehlschlägt. DirectorySlash Off +# Groessere Upload-/POST-Limits fuer den Daten-Export/-Import im Admin-Bereich +# (der Export bettet die Bildergalerie als Base64 direkt in die JSON-Datei ein, +# das kann schnell 40+ MB werden - PHPs Standard-Limits liegen oft niedriger). +# Nur relevant, wenn PHP als Apache-Modul laeuft (mod_php) - die IfModule-Pruefung +# sorgt dafuer, dass dieser Block auf FastCGI/PHP-FPM-Hosting (dort greift +# stattdessen api/public/.user.ini) folgenlos uebersprungen wird, statt einen +# "Invalid command"-Fehler fuer die ganze Seite auszuloesen. + + php_value upload_max_filesize 200M + php_value post_max_size 200M + php_value memory_limit 512M + + + php_value upload_max_filesize 200M + php_value post_max_size 200M + php_value memory_limit 512M + + + php_value upload_max_filesize 200M + php_value post_max_size 200M + php_value memory_limit 512M + + + php_value upload_max_filesize 200M + php_value post_max_size 200M + php_value memory_limit 512M + + # API-Requests an den PHP-Front-Controller weiterleiten RewriteCond %{REQUEST_URI} ^/api/ RewriteCond %{REQUEST_FILENAME} !-f diff --git a/hifi/api/public/.user.ini b/hifi/api/public/.user.ini new file mode 100644 index 0000000..167ddca --- /dev/null +++ b/hifi/api/public/.user.ini @@ -0,0 +1,8 @@ +; Groessere Upload-/POST-Limits fuer den Daten-Export/-Import im Admin-Bereich +; (der Export bettet die Bildergalerie als Base64 direkt in die JSON-Datei ein, +; das kann schnell 40+ MB werden). Greift auf PHP-FPM/CGI-Hosting (z.B. viele +; Shared-Hosting-Umgebungen) - fuer klassisches Apache-Modul-PHP (mod_php) +; uebernimmt stattdessen der php_value-Block in hifi/.htaccess. +upload_max_filesize = 200M +post_max_size = 200M +memory_limit = 512M diff --git a/hifi/api/src/Controllers/SettingsController.php b/hifi/api/src/Controllers/SettingsController.php index 025baa7..d277fc3 100644 --- a/hifi/api/src/Controllers/SettingsController.php +++ b/hifi/api/src/Controllers/SettingsController.php @@ -70,11 +70,53 @@ class SettingsController exit; } + /** Wandelt eine php.ini-Groessenangabe ("200M", "1G", "512K") in Bytes um. */ + private static function iniBytes(string $value): int + { + $value = trim($value); + if ($value === '') { + return 0; + } + $num = (float) $value; + return match (strtolower(substr($value, -1))) { + 'g' => (int) ($num * 1024 * 1024 * 1024), + 'm' => (int) ($num * 1024 * 1024), + 'k' => (int) ($num * 1024), + default => (int) $value, + }; + } + public static function importData(): void { - if (!empty($_FILES['file']) && $_FILES['file']['error'] === UPLOAD_ERR_OK) { + if (!empty($_FILES['file'])) { + $uploadError = $_FILES['file']['error']; + if ($uploadError === UPLOAD_ERR_INI_SIZE || $uploadError === UPLOAD_ERR_FORM_SIZE) { + Http::error( + 'Die Datei überschreitet das Upload-Limit dieses Servers (aktuell ' . ini_get('upload_max_filesize') . + '). Bitte den Hosting-Anbieter um ein höheres PHP-Upload-Limit bitten.', + 422 + ); + } + if ($uploadError !== UPLOAD_ERR_OK) { + Http::error('Datei-Upload fehlgeschlagen (Fehlercode ' . $uploadError . ').', 422); + } $raw = file_get_contents($_FILES['file']['tmp_name']); } else { + // Ein Upload, der post_max_size ueberschreitet, wird von PHP nicht in $_FILES + // aufgenommen - der rohe Body ist ueber php://input aber trotzdem da (nur eben + // noch als unverarbeitetes Multipart-Gemisch, kein gueltiges JSON). Deshalb direkt + // Content-Length gegen das konfigurierte Limit pruefen, statt block auf einen + // (nicht garantiert leeren) Body zu vertrauen - sonst landet eine zu grosse, aber + // eigentlich intakte Export-Datei im irrefuehrenden generischen "ungueltige Datei"-Fehler. + $contentLength = (int) ($_SERVER['CONTENT_LENGTH'] ?? 0); + $maxPost = self::iniBytes((string) ini_get('post_max_size')); + if ($maxPost > 0 && $contentLength > $maxPost) { + Http::error( + 'Die Datei überschreitet das Größen-Limit dieses Servers (aktuell ' . ini_get('post_max_size') . + '). Bitte den Hosting-Anbieter um ein höheres PHP-Upload-Limit (post_max_size) bitten.', + 422 + ); + } $raw = file_get_contents('php://input'); }