diff --git a/src/HeaderEnum.php b/src/HeaderEnum.php index 6aef573..fb3390b 100644 --- a/src/HeaderEnum.php +++ b/src/HeaderEnum.php @@ -7,6 +7,7 @@ enum HeaderEnum: string case CACHE_TAG = 'Cache-Tag'; case CACHE_PURGE_TAG = 'Cache-Purge-Tag'; case CACHE_PURGE_PREFIX = 'Cache-Purge-Prefix'; + case CACHE_PURGE_URL = 'Cache-Purge-URL'; case CACHE_CONTROL = 'Cache-Control'; case CDN_CACHE_CONTROL = 'CDN-Cache-Control'; case SURROGATE_CONTROL = 'Surrogate-Control'; diff --git a/src/StaticCache.php b/src/StaticCache.php index 588d235..1d694c1 100644 --- a/src/StaticCache.php +++ b/src/StaticCache.php @@ -14,7 +14,6 @@ use craft\web\UrlManager; use craft\web\View; use Illuminate\Support\Collection; -use League\Uri\Components\Path; use samdark\log\PsrMessage; use yii\base\Event; use yii\caching\TagDependency; @@ -38,12 +37,14 @@ class StaticCache extends \yii\base\Component private ?int $cacheDuration = null; private Collection $tags; private Collection $tagsToPurge; + private Collection $urlsToPurge; private bool $collectingCacheInfo = false; public function init(): void { $this->tags = Collection::make(); $this->tagsToPurge = Collection::make(); + $this->urlsToPurge = Collection::make(); } public function registerEventHandlers(): void @@ -99,6 +100,15 @@ public function registerEventHandlers(): void Craft::error('Failed to purge tags after request'); } } + + if ($this->urlsToPurge->isNotEmpty()) { + try { + $this->purgeUrls(...$this->urlsToPurge); + } catch (\Throwable $e) { + // TODO: log exception once output payload isn't a concern + Craft::error('Failed to purge tags after request'); + } + } }); } @@ -166,12 +176,12 @@ private function handleRegisterCacheOptions(RegisterCacheOptionsEvent $event): v private function handleSaveElement(ElementEvent $event): void { - $this->purgeElementUri($event->element); + $this->purgeElementUrl($event->element); } private function handleDeleteElement(ElementEvent $event): void { - $this->purgeElementUri($event->element); + $this->purgeElementUrl($event->element); } public function purgeAll(): void @@ -198,23 +208,15 @@ public function purgeCdn(): void $this->tagsToPurge->push($tag); } - private function purgeElementUri(ElementInterface $element): void + private function purgeElementUrl(ElementInterface $element): void { - $uri = $element->uri ?? null; + $url = $element->uri ?? null; - if (ElementHelper::isDraftOrRevision($element) || !$uri) { + if (ElementHelper::isDraftOrRevision($element) || !$url) { return; } - $uri = $element->getIsHomepage() - ? '/' - : Path::new($uri)->withLeadingSlash()->withoutTrailingSlash(); - - $tag = StaticCacheTag::create($uri) - ->withPrefix(Module::getInstance()->getConfig()->environmentId . ':') - ->minify(false); - - $this->tagsToPurge->prepend($tag); + $this->urlsToPurge->push($url); } private function addCacheHeadersToWebResponse(): void @@ -298,6 +300,24 @@ public function purgeTags(string|StaticCacheTag ...$tags): void ]); } + public function purgeUrls(string ...$urls): void + { + $urls = Collection::make($urls)->filter()->unique(); + + if ($urls->isEmpty()) { + return; + } + + Craft::info(new PsrMessage('Purging URLs', [ + 'urls' => $urls->all(), + ])); + + // TODO: make sure we don't go over max header size + Helper::makeGatewayApiRequest([ + HeaderEnum::CACHE_PURGE_URL->value => $urls->implode(','), + ]); + } + public function purgeUrlPrefixes(string ...$urlPrefixes): void { $urlPrefixes = Collection::make($urlPrefixes)->filter()->unique(); diff --git a/src/cli/controllers/StaticCacheController.php b/src/cli/controllers/StaticCacheController.php index c398296..aa3dd85 100644 --- a/src/cli/controllers/StaticCacheController.php +++ b/src/cli/controllers/StaticCacheController.php @@ -17,6 +17,15 @@ public function actionPurgePrefixes(string ...$prefixes): int return ExitCode::OK; } + public function actionPurgeUrls(string ...$urls): int + { + $this->do('Purging URLs', function() use ($urls) { + Module::getInstance()->getStaticCache()->purgeUrls(...$urls); + }); + + return ExitCode::OK; + } + public function actionPurgeTags(string ...$tags): int { $this->do('Purging tags', function() use ($tags) {