'Not authenticated'], 401); } if ($_SERVER['REQUEST_METHOD'] !== 'POST') { json_response(['error' => 'POST only'], 405); } csrf_verify(); // One image per request; a single file may still be large, so lift the time cap. @set_time_limit(0); // When a request body exceeds post_max_size, PHP discards $_POST and $_FILES // entirely — surface that as a clear 413 instead of a misleading "no file". if ((int)($_SERVER['CONTENT_LENGTH'] ?? 0) > 0 && !$_POST && !$_FILES) { json_response(['error' => 'Upload exceeds the server post_max_size limit'], 413); } $gallery = gallery_load((string)($_POST['slug'] ?? '')); if ($gallery === null) { json_response(['error' => 'Unknown gallery'], 404); } // Trusted admin path: any file type is allowed (imagesOnly stays false). [$status, $payload] = gallery_store_s3_upload( $gallery, $_FILES['original'] ?? null, $_FILES['thumb'] ?? null ); json_response($payload, $status);