diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a86f226dd7..2ed2b65ff7 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -38,9 +38,6 @@ jobs: - name: Run Tests run: docker compose exec -T appwrite test --debug - - name: Logs - run: docker compose logs appwrite - - name: Teardown if: always() run: | diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index e88dd90f20..eda55b6c8c 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -802,7 +802,6 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') ->alias('/v1/storage/files/:fileId/preview', ['bucketId' => 'default']) ->desc('Get File Preview') ->groups(['api', 'storage']) - ->label('cache', true) ->label('scope', 'files.read') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'storage') @@ -863,6 +862,9 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') $outputs = Config::getParam('storage-outputs'); $fileLogos = Config::getParam('storage-logos'); + $date = \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT'; // 45 days cache + $key = \md5($fileId . $width . $height . $gravity . $quality . $borderWidth . $borderColor . $borderRadius . $opacity . $rotation . $background . $output); + if ($bucket->getAttribute('permission') === 'bucket') { // skip authorization $file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); @@ -879,6 +881,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') $algorithm = $file->getAttribute('algorithm'); $cipher = $file->getAttribute('openSSLCipher'); $mime = $file->getAttribute('mimeType'); + if (!\in_array($mime, $inputs) || $file->getAttribute('sizeActual') > (int) App::getEnv('_APP_STORAGE_PREVIEW_LIMIT', 20000000)) { if (!\in_array($mime, $inputs)) { $path = (\array_key_exists($mime, $fileLogos)) ? $fileLogos[$mime] : $fileLogos['default']; @@ -891,6 +894,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') $cipher = null; $background = (empty($background)) ? 'eceff1' : $background; $type = \strtolower(\pathinfo($path, PATHINFO_EXTENSION)); + $key = \md5($path . $width . $height . $gravity . $quality . $borderWidth . $borderColor . $borderRadius . $opacity . $rotation . $background . $output); $deviceFiles = $deviceLocal; } @@ -901,12 +905,23 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') throw new Exception('File not found', 404, Exception::STORAGE_FILE_NOT_FOUND); } + $cache = new Cache(new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $project->getId() . DIRECTORY_SEPARATOR . $bucketId . DIRECTORY_SEPARATOR . $fileId)); // Limit file number or size + $data = $cache->load($key, 60 * 60 * 24 * 30 * 3/* 3 months */); + if (empty($output)) { // when file extension is not provided and the mime type is not one of our supported outputs // we fallback to `jpg` output format $output = empty($type) ? (array_search($mime, $outputs) ?? 'jpg') : $type; } + if ($data) { + return $response + ->setContentType((\array_key_exists($output, $outputs)) ? $outputs[$output] : $outputs['jpg']) + ->addHeader('Expires', $date) + ->addHeader('X-Appwrite-Cache', 'hit') + ->send($data) + ; + } $source = $deviceFiles->read($path); @@ -951,18 +966,20 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') $data = $image->output($output, $quality); + $cache->save($key, $data); + $usage ->setParam('storage.files.read', 1) ->setParam('bucketId', $bucketId) ; - $contentType = (\array_key_exists($output, $outputs)) ? $outputs[$output] : $outputs['jpg']; - $response - ->addHeader('Expires', \date('D, d M Y H:i:s', \time() + 60 * 60 * 24 * 30) . ' GMT') - ->setContentType($contentType) - ->file($data) + ->setContentType((\array_key_exists($output, $outputs)) ? $outputs[$output] : $outputs['jpg']) + ->addHeader('Expires', $date) + ->addHeader('X-Appwrite-Cache', 'miss') + ->send($data) ; + unset($image); });