From 07cc9f8a2625ff1abb0d85f605d515de099b00e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Sun, 30 Apr 2023 09:28:37 +0200 Subject: [PATCH] Implement card cache busting --- app/controllers/api/account.php | 6 +++++ app/controllers/api/avatars.php | 9 +++----- app/controllers/api/users.php | 4 ++++ app/controllers/shared/api.php | 39 +++++++++++++++++++++++++++++++++ 4 files changed, 52 insertions(+), 6 deletions(-) diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index d5d9539a5f..c3c75dd7ae 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -473,6 +473,8 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') ->label('docs', false) ->label('usage.metric', 'sessions.{scope}.requests.create') ->label('usage.params', ['provider:{request.provider}']) + ->label('cacheBuster', true) + ->label('cacheBuster.resource', ['card-cloud/{user.$id}', 'card-cloud-back/{user.$id}', 'card-cloud-og/{user.$id}']) ->param('provider', '', new WhiteList(\array_keys(Config::getParam('providers')), true), 'OAuth2 provider.') ->param('code', '', new Text(2048), 'OAuth2 code.') ->param('state', '', new Text(2048), 'OAuth2 state params.', true) @@ -1568,6 +1570,8 @@ App::patch('/v1/account/name') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_ACCOUNT) + ->label('cacheBuster', true) + ->label('cacheBuster.resource', ['card-cloud/{user.$id}', 'card-cloud-back/{user.$id}', 'card-cloud-og/{user.$id}']) ->param('name', '', new Text(128), 'User name. Max length: 128 chars.') ->inject('response') ->inject('user') @@ -1639,6 +1643,8 @@ App::patch('/v1/account/email') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_ACCOUNT) + ->label('cacheBuster', true) + ->label('cacheBuster.resource', ['card-cloud/{user.$id}', 'card-cloud-back/{user.$id}', 'card-cloud-og/{user.$id}']) ->param('email', '', new Email(), 'User email.') ->param('password', '', new Password(), 'User password. Must be at least 8 chars.') ->inject('response') diff --git a/app/controllers/api/avatars.php b/app/controllers/api/avatars.php index 4801d87b7a..daa535e734 100644 --- a/app/controllers/api/avatars.php +++ b/app/controllers/api/avatars.php @@ -558,8 +558,7 @@ App::get('/v1/cards/cloud') ->groups(['api', 'avatars']) ->label('scope', 'avatars.read') ->label('cache', true) - ->label('cache.resourceType', 'cards/cloud') - ->label('cache.resource', 'card/{request.userId}') + ->label('cache.resource', 'card-cloud/{request.userId}') ->label('docs', false) ->label('origin', '*') ->param('userId', '', new UID(), 'User ID.', true) @@ -765,8 +764,7 @@ App::get('/v1/cards/cloud-back') ->groups(['api', 'avatars']) ->label('scope', 'avatars.read') ->label('cache', true) - ->label('cache.resourceType', 'cards/cloud-back') - ->label('cache.resource', 'card-back/{request.userId}') + ->label('cache.resource', 'card-cloud-back/{request.userId}') ->label('docs', false) ->label('origin', '*') ->param('userId', '', new UID(), 'User ID.', true) @@ -843,8 +841,7 @@ App::get('/v1/cards/cloud-og') ->groups(['api', 'avatars']) ->label('scope', 'avatars.read') ->label('cache', true) - ->label('cache.resourceType', 'cards/cloud-og') - ->label('cache.resource', 'card-og/{request.userId}') + ->label('cache.resource', 'card-cloud-og/{request.userId}') ->label('docs', false) ->label('origin', '*') ->param('userId', '', new UID(), 'User ID.', true) diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index dce493b024..81419c296e 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -737,6 +737,8 @@ App::patch('/v1/users/:userId/name') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_USER) + ->label('cacheBuster', true) + ->label('cacheBuster.resource', ['card-cloud/{user.$id}', 'card-cloud-back/{user.$id}', 'card-cloud-og/{user.$id}']) ->param('userId', '', new UID(), 'User ID.') ->param('name', '', new Text(128), 'User name. Max length: 128 chars.') ->inject('response') @@ -820,6 +822,8 @@ App::patch('/v1/users/:userId/email') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_USER) + ->label('cacheBuster', true) + ->label('cacheBuster.resource', ['card-cloud/{user.$id}', 'card-cloud-back/{user.$id}', 'card-cloud-og/{user.$id}']) ->param('userId', '', new UID(), 'User ID.') ->param('email', '', new Email(), 'User email.') ->inject('response') diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index db35cebc65..58b0279b24 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -21,6 +21,7 @@ use Utopia\CLI\Console; use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Document; +use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; $parseLabel = function (string $label, array $responsePayload, array $requestParams, Document $user) { @@ -437,6 +438,43 @@ App::shutdown() $database->trigger(); } + /** + * Cache Buster + */ + $bustCache = $route->getLabel('cacheBuster', false); + if ($bustCache) { + $payload = $response->getPayload(); + + if (!empty($payload)) { + $resources = []; + + $patterns = $route->getLabel('cacheBuster.resource', []); + if (!empty($patterns)) { + foreach ($patterns as $pattern) { + $resources[] = $parseLabel($pattern, $responsePayload, $requestParams, $user); + } + } + + $caches = Authorization::skip(fn () => $dbForProject->find('cache', [ + Query::equal('resource', $resources), + Query::limit(100) + ])); + + foreach ($caches as $cache) { + $key = $cache->getId(); + \var_dump("Pruning " . $key); + + $cache = new Cache( + new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $project->getId()) + ); + + $cache->purge($key); + + Authorization::skip(fn () => $dbForProject->deleteDocument('cache', $cache->getId())); + } + } + } + /** * Cache label */ @@ -468,6 +506,7 @@ App::shutdown() $cacheLog = $dbForProject->getDocument('cache', $key); $accessedAt = $cacheLog->getAttribute('accessedAt', ''); $now = DateTime::now(); + if ($cacheLog->isEmpty()) { Authorization::skip(fn () => $dbForProject->createDocument('cache', new Document([ '$id' => $key,