diff --git a/app/controllers/api/avatars.php b/app/controllers/api/avatars.php index deb7a0c36..0ba0af37c 100644 --- a/app/controllers/api/avatars.php +++ b/app/controllers/api/avatars.php @@ -20,7 +20,7 @@ use Utopia\Validator\Range; use Utopia\Validator\Text; use Utopia\Validator\WhiteList; -$avatarCallback = function (string $type, string $code, int $width, int $height, int $quality, Response $response, Database $dbForProject) { +$avatarCallback = function (string $type, string $code, int $width, int $height, int $quality, Response $response) { $code = \strtolower($code); $type = \strtolower($type); @@ -52,18 +52,8 @@ $avatarCallback = function (string $type, string $code, int $width, int $height, $data = $cache->load($key, 60 * 60 * 24 * 30 * 3/* 3 months */); if ($data) { //$output = (empty($output)) ? $type : $output; - - $fileCache = $dbForProject->getDocument('cache', $key); - if ($fileCache->isEmpty()) { - Authorization::skip(fn () => $dbForProject->createDocument('cache', new Document([ - '$id' => $key, - 'accessedAt' => time(), - 'path' => 'app-0' - ]))); - } else { - $fileCache->setAttribute('accessedAt', time()); - Authorization::skip(fn () => $dbForProject->updateDocument('cache', $fileCache->getId(), $fileCache)); - } + App::setResource('cacheKey', fn () => $key); + App::setResource('cachePath', fn () => 'app-0'); return $response ->setContentType('image/png') @@ -93,7 +83,7 @@ $avatarCallback = function (string $type, string $code, int $width, int $height, App::get('/v1/avatars/credit-cards/:code') ->desc('Get Credit Card Icon') - ->groups(['api', 'avatars']) + ->groups(['api', 'avatars', 'cache']) ->label('scope', 'avatars.read') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'avatars') @@ -107,12 +97,11 @@ App::get('/v1/avatars/credit-cards/:code') ->param('height', 100, new Range(0, 2000), 'Image height. Pass an integer between 0 to 2000. Defaults to 100.', true) ->param('quality', 100, new Range(0, 100), 'Image quality. Pass an integer between 0 to 100. Defaults to 100.', true) ->inject('response') - ->inject('dbForProject') - ->action(fn (string $code, int $width, int $height, int $quality, Response $response, Database $dbForProject) => $avatarCallback('credit-cards', $code, $width, $height, $quality, $response, $dbForProject)); + ->action(fn (string $code, int $width, int $height, int $quality, Response $response) => $avatarCallback('credit-cards', $code, $width, $height, $quality, $response)); App::get('/v1/avatars/browsers/:code') ->desc('Get Browser Icon') - ->groups(['api', 'avatars']) + ->groups(['api', 'avatars', 'cache']) ->label('scope', 'avatars.read') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'avatars') @@ -126,12 +115,11 @@ App::get('/v1/avatars/browsers/:code') ->param('height', 100, new Range(0, 2000), 'Image height. Pass an integer between 0 to 2000. Defaults to 100.', true) ->param('quality', 100, new Range(0, 100), 'Image quality. Pass an integer between 0 to 100. Defaults to 100.', true) ->inject('response') - ->inject('dbForProject') - ->action(fn (string $code, int $width, int $height, int $quality, Response $response, Database $dbForProject) => $avatarCallback('browsers', $code, $width, $height, $quality, $response, $dbForProject)); + ->action(fn (string $code, int $width, int $height, int $quality, Response $response) => $avatarCallback('browsers', $code, $width, $height, $quality, $response)); App::get('/v1/avatars/flags/:code') ->desc('Get Country Flag') - ->groups(['api', 'avatars']) + ->groups(['api', 'avatars', 'cache']) ->label('scope', 'avatars.read') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'avatars') @@ -145,8 +133,7 @@ App::get('/v1/avatars/flags/:code') ->param('height', 100, new Range(0, 2000), 'Image height. Pass an integer between 0 to 2000. Defaults to 100.', true) ->param('quality', 100, new Range(0, 100), 'Image quality. Pass an integer between 0 to 100. Defaults to 100.', true) ->inject('response') - ->inject('dbForProject') - ->action(fn (string $code, int $width, int $height, int $quality, Response $response, Database $dbForProject) => $avatarCallback('flags', $code, $width, $height, $quality, $response, $dbForProject)); + ->action(fn (string $code, int $width, int $height, int $quality, Response $response) => $avatarCallback('flags', $code, $width, $height, $quality, $response)); App::get('/v1/avatars/image') ->desc('Get Image from URL') @@ -163,8 +150,7 @@ App::get('/v1/avatars/image') ->param('width', 400, new Range(0, 2000), 'Resize preview image width, Pass an integer between 0 to 2000. Defaults to 400.', true) ->param('height', 400, new Range(0, 2000), 'Resize preview image height, Pass an integer between 0 to 2000. Defaults to 400.', true) ->inject('response') - ->inject('dbForProject') - ->action(function (string $url, int $width, int $height, Response $response, Database $dbForProject) { + ->action(function (string $url, int $width, int $height, Response $response) { $quality = 80; $output = 'png'; @@ -175,19 +161,8 @@ App::get('/v1/avatars/image') $data = $cache->load($key, 60 * 60 * 24 * 7/* 1 week */); if ($data) { - $fileCache = $dbForProject->getDocument('cache', $key); - - if ($fileCache->isEmpty()) { - Authorization::skip(fn () => $dbForProject->createDocument('cache', new Document([ - '$id' => $key, - 'accessedAt' => time(), - 'path' => 'app-0' - - ]))); - } else { - $fileCache->setAttribute('accessedAt', time()); - Authorization::skip(fn () => $dbForProject->updateDocument('cache', $fileCache->getId(), $fileCache)); - } + App::setResource('cacheKey', fn () => $key); + App::setResource('cachePath', fn () => 'app-0'); return $response ->setContentType('image/png') @@ -231,7 +206,7 @@ App::get('/v1/avatars/image') App::get('/v1/avatars/favicon') ->desc('Get Favicon') - ->groups(['api', 'avatars']) + ->groups(['api', 'avatars', 'cache']) ->label('scope', 'avatars.read') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'avatars') @@ -242,8 +217,7 @@ App::get('/v1/avatars/favicon') ->label('sdk.response.type', Response::CONTENT_TYPE_IMAGE) ->param('url', '', new URL(['http', 'https']), 'Website URL which you want to fetch the favicon from.') ->inject('response') - ->inject('dbForProject') - ->action(function (string $url, Response $response, Database $dbForProject) { + ->action(function (string $url, Response $response) { $width = 56; $height = 56; @@ -255,18 +229,8 @@ App::get('/v1/avatars/favicon') $cache = new Cache(new Filesystem(APP_STORAGE_CACHE . '/app-0')); // Limit file number or size $data = $cache->load($key, 60 * 60 * 24 * 30 * 3/* 3 months */); if ($data) { - $fileCache = $dbForProject->getDocument('cache', $key); - - if ($fileCache->isEmpty()) { - Authorization::skip(fn () => $dbForProject->createDocument('cache', new Document([ - '$id' => $key, - 'accessedAt' => time(), - 'path' => 'app-0' - ]))); - } else { - $fileCache->setAttribute('accessedAt', time()); - Authorization::skip(fn () => $dbForProject->updateDocument('cache', $fileCache->getId(), $fileCache)); - } + App::setResource('cacheKey', fn () => $key); + App::setResource('cachePath', fn () => 'app-0'); return $response ->setContentType('image/png') diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index 99df2cc87..d41181b37 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -153,7 +153,6 @@ App::post('/v1/projects') 'orders' => $index['orders'], ]); } - var_dump($key); $dbForProject->createCollection($key, $attributes, $indexes); } diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index ff5420ad7..b9acfc850 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -122,7 +122,6 @@ App::post('/v1/storage/buckets') $bucket = $dbForProject->getDocument('buckets', $bucketId); $dbForProject->createCollection('bucket_' . $bucket->getInternalId(), $attributes, $indexes); - } catch (Duplicate $th) { throw new Exception('Bucket already exists', 409, Exception::STORAGE_BUCKET_ALREADY_EXISTS); } @@ -611,6 +610,7 @@ App::post('/v1/storage/buckets/:bucketId/files') '$id' => $fileId, '$read' => $read, '$write' => $write, + 'dateCreated' => \time(), 'bucketId' => $bucket->getId(), 'name' => $fileName, 'path' => $path, @@ -801,7 +801,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId') App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') ->alias('/v1/storage/files/:fileId/preview', ['bucketId' => 'default']) ->desc('Get File Preview') - ->groups(['api', 'storage']) + ->groups(['api', 'storage', 'cache']) ->label('scope', 'files.read') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'storage') @@ -907,6 +907,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') $cache = new Cache(new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $project->getId())); // 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 @@ -914,18 +915,8 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') } if ($data) { - $fileCache = $dbForProject->getDocument('cache', $key); - - if ($fileCache->isEmpty()) { - Authorization::skip(fn () => $dbForProject->createDocument('cache', new Document([ - '$id' => $key, - 'accessedAt' => time(), - 'path' => 'app-' . $project->getId() - ]))); - } else { - $fileCache->setAttribute('accessedAt', time()); - Authorization::skip(fn () => $dbForProject->updateDocument('cache', $fileCache->getId(), $fileCache)); - } + App::setResource('cacheKey', fn () => $key); + App::setResource('cachePath', fn () => 'app-' . $project->getId()); return $response ->setContentType((\array_key_exists($output, $outputs)) ? $outputs[$output] : $outputs['jpg']) diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 3f5f2277d..b22cd9d96 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -165,7 +165,7 @@ App::init(function (App $utopia, Request $request, Document $project) { } }, ['utopia', 'request', 'project'], 'auth'); -App::shutdown(function (App $utopia, Request $request, Response $response, Document $project, Event $events, Audit $audits, Stats $usage, Delete $deletes, EventDatabase $database, string $mode, Database $dbForProject) { +App::shutdown(function (App $utopia, Request $request, Response $response, Document $project, Event $events, Audit $audits, Stats $usage, Delete $deletes, EventDatabase $database, string $mode, Database $dbForProject, string $cacheKey, string $cachePath) { if (!empty($events->getEvent())) { if (empty($events->getPayload())) { @@ -237,7 +237,26 @@ App::shutdown(function (App $utopia, Request $request, Response $response, Docum $database->trigger(); } + $route = $utopia->match($request); + $groups = $route->getGroups(); + if (in_array('cache', $groups)) { + if (!empty($cacheKey) && !empty($cachePath)) { + $cacheLog = $dbForProject->getDocument('cache', $cacheKey); + if ($cacheLog->isEmpty()) { + Authorization::skip(fn () => $dbForProject->createDocument('cache', new Document([ + '$id' => $cacheKey, + 'accessedAt' => time(), + 'path' => $cachePath + ]))); + } else { + $cacheLog->setAttribute('accessedAt', time()); + Authorization::skip(fn () => $dbForProject->updateDocument('cache', $cacheLog->getId(), $cacheLog)); + } + } + } + + if ( App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled' && $project->getId() @@ -249,4 +268,4 @@ App::shutdown(function (App $utopia, Request $request, Response $response, Docum ->setParam('networkResponseSize', $response->getSize()) ->submit(); } -}, ['utopia', 'request', 'response', 'project', 'events', 'audits', 'usage', 'deletes', 'database', 'mode', 'dbForProject'], 'api'); +}, ['utopia', 'request', 'response', 'project', 'events', 'audits', 'usage', 'deletes', 'database', 'mode', 'dbForProject', 'cacheKey', 'cachePath'], 'api'); diff --git a/app/init.php b/app/init.php index 4c27308df..bac8bb089 100644 --- a/app/init.php +++ b/app/init.php @@ -86,8 +86,8 @@ const APP_LIMIT_COMPRESSION = 20000000; //20MB const APP_LIMIT_ARRAY_PARAMS_SIZE = 100; // Default maximum of how many elements can there be in API parameter that expects array value const APP_LIMIT_ARRAY_ELEMENT_SIZE = 4096; // Default maximum length of element in array parameter represented by maximum URL length. const APP_LIMIT_SUBQUERY = 1000; -const APP_CACHE_BUSTER = 400; -const APP_VERSION_STABLE = '0.15.0'; +const APP_CACHE_BUSTER = 402; +const APP_VERSION_STABLE = '0.15.2'; const APP_DATABASE_ATTRIBUTE_EMAIL = 'email'; const APP_DATABASE_ATTRIBUTE_ENUM = 'enum'; const APP_DATABASE_ATTRIBUTE_IP = 'ip'; @@ -96,7 +96,6 @@ const APP_DATABASE_ATTRIBUTE_INT_RANGE = 'intRange'; const APP_DATABASE_ATTRIBUTE_FLOAT_RANGE = 'floatRange'; const APP_DATABASE_ATTRIBUTE_STRING_MAX_LENGTH = 1073741824; // 2^32 bits / 4 bits per char const APP_STORAGE_UPLOADS = '/storage/uploads'; -const APP_STORAGE_VIDEO = '/storage/video'; const APP_STORAGE_FUNCTIONS = '/storage/functions'; const APP_STORAGE_BUILDS = '/storage/builds'; const APP_STORAGE_CACHE = '/storage/cache'; @@ -164,32 +163,6 @@ App::setMode(App::getEnv('_APP_ENV', App::MODE_TYPE_PRODUCTION)); /* * ENV vars */ -Config::load('renditions', __DIR__ . '/config/renditions.php'); -Config::load('events', __DIR__ . '/config/events.php'); -Config::load('auth', __DIR__ . '/config/auth.php'); -Config::load('errors', __DIR__ . '/config/errors.php'); -Config::load('providers', __DIR__ . '/config/providers.php'); -Config::load('platforms', __DIR__ . '/config/platforms.php'); -Config::load('collections', __DIR__ . '/config/collections.php'); -Config::load('runtimes', __DIR__ . '/config/runtimes.php'); -Config::load('roles', __DIR__ . '/config/roles.php'); // User roles and scopes -Config::load('scopes', __DIR__ . '/config/scopes.php'); // User roles and scopes -Config::load('services', __DIR__ . '/config/services.php'); // List of services -Config::load('variables', __DIR__ . '/config/variables.php'); // List of env variables -Config::load('avatar-browsers', __DIR__ . '/config/avatars/browsers.php'); -Config::load('avatar-credit-cards', __DIR__ . '/config/avatars/credit-cards.php'); -Config::load('avatar-flags', __DIR__ . '/config/avatars/flags.php'); -Config::load('locale-codes', __DIR__ . '/config/locale/codes.php'); -Config::load('locale-currencies', __DIR__ . '/config/locale/currencies.php'); -Config::load('locale-eu', __DIR__ . '/config/locale/eu.php'); -Config::load('locale-languages', __DIR__ . '/config/locale/languages.php'); -Config::load('locale-phones', __DIR__ . '/config/locale/phones.php'); -Config::load('locale-countries', __DIR__ . '/config/locale/countries.php'); -Config::load('locale-continents', __DIR__ . '/config/locale/continents.php'); -Config::load('storage-logos', __DIR__ . '/config/storage/logos.php'); -Config::load('storage-mimes', __DIR__ . '/config/storage/mimes.php'); -Config::load('storage-inputs', __DIR__ . '/config/storage/inputs.php'); -Config::load('storage-outputs', __DIR__ . '/config/storage/outputs.php'); Config::load('events', __DIR__ . '/config/events.php'); Config::load('auth', __DIR__ . '/config/auth.php'); Config::load('errors', __DIR__ . '/config/errors.php');