Skip to content

Commit

Permalink
Use Flarum events to purge LSCache to support CLI and HTTP contexts (#30
Browse files Browse the repository at this point in the history
)
  • Loading branch information
rafaucau authored Sep 14, 2024
1 parent 4e4ef56 commit 41cd90d
Show file tree
Hide file tree
Showing 43 changed files with 969 additions and 604 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/backend.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ jobs:
with:
enable_backend_testing: false
enable_phpstan: true
php_versions: '["8.0", "8.1", "8.2", "8.3"]'
php_versions: '["8.1", "8.2", "8.3"]'

backend_directory: .
17 changes: 13 additions & 4 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,13 @@
},
"require-dev": {
"flarum/phpstan": "^1.8",
"flarum/tags": "*",
"v17development/flarum-blog": "^0.7.7"
"flarum/tags": "^1.8",
"v17development/flarum-blog": "^0.7.7",
"flarum/approval": "^1.8",
"flarum/likes": "^1.8",
"fof/masquerade": "^2.1",
"clarkwinkelmann/flarum-ext-author-change": "^1.0",
"sycho/flarum-move-posts": "^0.1.7"
},
"suggest": {
"blomstra/flarum-redis": "This library allows using Redis as cache, session and for the queue. https://github.com/blomstra/flarum-redis#set-up"
Expand All @@ -41,7 +46,7 @@
],
"autoload": {
"psr-4": {
"ACPL\\FlarumCache\\": "src/"
"ACPL\\FlarumLSCache\\": "src/"
}
},
"extra": {
Expand All @@ -54,9 +59,13 @@
"color": "#fff"
},
"optional-dependencies": [
"flarum/approval",
"flarum/tags",
"flarum/likes",
"fof/masquerade"
"fof/masquerade",
"v17development/flarum-blog",
"clarkwinkelmann/flarum-ext-author-change",
"sycho/flarum-move-posts"
]
},
"flarum-cli": {
Expand Down
88 changes: 51 additions & 37 deletions extend.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,32 @@
* file that was distributed with this source code.
*/

namespace ACPL\FlarumCache;
namespace ACPL\FlarumLSCache;

use ACPL\FlarumCache\Api\Controller\LSCacheCsrfResponseController;
use ACPL\FlarumCache\Api\Controller\PurgeLSCacheController;
use ACPL\FlarumCache\Command\LSCacheClearCommand;
use ACPL\FlarumCache\Compatibility\Flarum\Likes\FlarumLikesPurgeMiddleware;
use ACPL\FlarumCache\Compatibility\Flarum\Tags\FlarumTagsPurgeMiddleware;
use ACPL\FlarumCache\Compatibility\FriendsOfFlarum\Masquerade\FofMasqueradePurgeMiddleware;
use ACPL\FlarumCache\Compatibility\v17development\FlarumBlog\FlarumBlogPurgeMiddleware;
use ACPL\FlarumCache\Listener\ClearingCacheListener;
use ACPL\FlarumCache\Middleware\LoginMiddleware;
use ACPL\FlarumCache\Middleware\LogoutMiddleware;
use ACPL\FlarumCache\Middleware\LSCacheControlMiddleware;
use ACPL\FlarumCache\Middleware\LSCachePurgeMiddleware;
use ACPL\FlarumCache\Middleware\LSTagsMiddleware;
use ACPL\FlarumCache\Middleware\VaryCookieMiddleware;
use ACPL\FlarumLSCache\Api\Controller\{LSCacheCsrfResponseController, PurgeLSCacheController};
use ACPL\FlarumLSCache\Command\LSCachePurgeCommand;
use ACPL\FlarumLSCache\Compatibility\{
ClarkWinkelmann\AuthorChange\ClarkWinkelmannAuthorChangeEventSubscriber,
Flarum\Likes\FlarumLikesEventSubscriber,
Flarum\Tags\FlarumTagsEventSubscriber,
FriendsOfFlarum\Masquerade\FofMasqueradePurgeCacheMiddleware,
SychO\MovePosts\SychOMovePostsSubscriber,
v17development\FlarumBlog\FlarumBlogEventSubscriber
};
use ACPL\FlarumLSCache\Listener\{
ClearingCacheListener,
DiscussionEventSubscriber,
PostEventSubscriber,
UserEventSubscriber
};
use ACPL\FlarumLSCache\Middleware\{
CacheControlMiddleware,
CacheTagsMiddleware,
LoginMiddleware,
LogoutMiddleware,
PurgeCacheMiddleware,
VaryCookieMiddleware
};
use Flarum\Extend;
use Flarum\Foundation\Event\ClearingCache;
use Flarum\Http\Middleware\CheckCsrfToken;
Expand All @@ -41,7 +51,7 @@
->default('acpl-lscache.public_cache_ttl', 604_800)
->default('acpl-lscache.clearing_cache_listener', true)
->default('acpl-lscache.drop_qs', implode("\n", LSCache::DEFAULT_DROP_QS)),
(new Extend\Event())->listen(Saved::class, Listener\UpdateSettings::class),
(new Extend\Event())->listen(Saved::class, Listener\UpdateSettingsListener::class),

// Vary cookie
(new Extend\Middleware('forum'))->insertAfter(CheckCsrfToken::class, VaryCookieMiddleware::class),
Expand All @@ -53,43 +63,47 @@
(new Extend\Middleware('forum'))->insertAfter(VaryCookieMiddleware::class, LogoutMiddleware::class),

// Tag routes
(new Extend\Middleware('forum'))->add(LSTagsMiddleware::class),
(new Extend\Middleware('api'))->add(LSTagsMiddleware::class),
(new Extend\Middleware('forum'))->add(CacheTagsMiddleware::class),
(new Extend\Middleware('api'))->add(CacheTagsMiddleware::class),

// Cache routes
(new Extend\Middleware('forum'))->insertAfter(VaryCookieMiddleware::class, LSCacheControlMiddleware::class),
(new Extend\Middleware('api'))->insertAfter(VaryCookieMiddleware::class, LSCacheControlMiddleware::class),
(new Extend\Middleware('forum'))->insertAfter(VaryCookieMiddleware::class, CacheControlMiddleware::class),
(new Extend\Middleware('api'))->insertAfter(VaryCookieMiddleware::class, CacheControlMiddleware::class),

// A workaround for the CSRF cache issue. The JS script fetches this path to update the CSRF
(new Extend\Routes('api'))->get('/lscache-csrf', 'lscache.csrf', LSCacheCsrfResponseController::class),

// Purge cache on update
(new Extend\Middleware('forum'))->add(LSCachePurgeMiddleware::class),
(new Extend\Middleware('admin'))->add(LSCachePurgeMiddleware::class),
(new Extend\Middleware('api'))->add(LSCachePurgeMiddleware::class),
(new Extend\Middleware('forum'))->add(PurgeCacheMiddleware::class),
(new Extend\Middleware('admin'))->add(PurgeCacheMiddleware::class),
(new Extend\Middleware('api'))->add(PurgeCacheMiddleware::class),

// Purge cache
(new Extend\Routes('api'))->get('/lscache-purge', 'lscache.purge', PurgeLSCacheController::class),
(new Extend\Console())->command(LSCacheClearCommand::class),
(new Extend\Event())->listen(ClearingCache::class, ClearingCacheListener::class),
(new Extend\Console)->command(LSCachePurgeCommand::class),
(new Extend\Event)->listen(ClearingCache::class, ClearingCacheListener::class),

(new Extend\Event)->subscribe(DiscussionEventSubscriber::class),
(new Extend\Event)->subscribe(PostEventSubscriber::class),
(new Extend\Event)->subscribe(UserEventSubscriber::class),

// Compatibility with extensions
(new Extend\Conditional)
->whenExtensionEnabled('flarum-tags', [
(new Extend\Middleware('api'))->add(FlarumTagsPurgeMiddleware::class),
])
->whenExtensionEnabled('flarum-likes', [
(new Extend\Middleware('api'))->add(FlarumLikesPurgeMiddleware::class),
(new Extend\Event)->subscribe(FlarumLikesEventSubscriber::class),
])
->whenExtensionEnabled('flarum-tags', [
(new Extend\Event)->subscribe(FlarumTagsEventSubscriber::class),
])
->whenExtensionEnabled('fof-masquerade', [
(new Extend\Middleware('api'))->add(FofMasqueradePurgeMiddleware::class),
(new Extend\Middleware('api'))->add(FofMasqueradePurgeCacheMiddleware::class),
])
->whenExtensionEnabled('v17development-blog', [
// Using insertBefore enables reading headers set by LSCachePurgeMiddleware, while insertAfter does not.
// This suggests Flarum processes middleware in a reverse order 🤔.
(new Extend\Middleware('api'))->insertBefore(
LSCachePurgeMiddleware::class,
FlarumBlogPurgeMiddleware::class
),
(new Extend\Event)->subscribe(FlarumBlogEventSubscriber::class),
])
->whenExtensionEnabled('clarkwinkelmann-author-change', [
(new Extend\Event)->subscribe(ClarkWinkelmannAuthorChangeEventSubscriber::class),
])
->whenExtensionEnabled('sycho-move-posts', [
(new Extend\Event)->subscribe(SychOMovePostsSubscriber::class),
]),
];
4 changes: 2 additions & 2 deletions locale/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ acpl-lscache:
serve_stale_label: "Serve Stale Content"
serve_stale_help: "If enabled, an outdated version of a cached page will be served to visitors until a fresh cache copy is generated. This reduces server load. If disabled, the page will be dynamically generated during the cache update, which may increase wait times. By design, this option can serve out-of-date content. Please do not enable this if you find that unacceptable."

purge_on_discussion_update_label: "Purge URLs or Tags on Discussion or Post Update"
purge_on_discussion_update_help: "Enter the URLs or Tags you want to purge when a discussion or post is updated, one per line. URL should start with <code>/</code>, e.g. <code>/rankings</code>, and cache Tag should start with <code>tag=</code>, e.g. <code>tag=rankings</code>. For multiple routes, adding a rule in .htaccess with a regular expression that tags routes and entering only this tag here is faster. <a>Learn more</a>. By default, the cache for the homepage and updated discussions is purged."
purge_on_discussion_update_label: "Purge URLs or cache Tags on Discussion Update"
purge_on_discussion_update_help: "Enter the URLs or cache Tags you want to purge when a discussion is updated, one per line. URL should start with <code>/</code>, e.g. <code>/rankings</code>, and cache Tag should start with <code>tag=</code>, e.g. <code>tag=rankings</code>. For multiple routes, adding a rule in .htaccess with a regular expression that tags routes and entering only this tag here is faster. <a>Learn more</a>. By default, the cache for the homepage and updated discussions is purged."

cache_exclude_label: "Exclude Paths from Caching"
cache_exclude_help: "Paths containing these strings will not be cached. For <code>/mypath/mypage?aa=bb</code>, you can use <code>mypage?aa=</code>. To match the beginning, add <code>^</code> at the start. For an exact match, add <code>$</code> at the end of the URL. One per line."
Expand Down
4 changes: 2 additions & 2 deletions migrations/2023_05_16_000001_update_htaccess.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

use ACPL\FlarumCache\Utility\HtaccessManager;
use ACPL\FlarumLSCache\Utility\HtaccessManager;
use Flarum\Foundation\Paths;
use Flarum\Http\CookieFactory;
use Flarum\Settings\SettingsRepositoryInterface;
Expand All @@ -22,5 +22,5 @@ function lsCacheGetHtaccessManager(): HtaccessManager
'down' => function () {
$htaccessManager = lsCacheGetHtaccessManager();
$htaccessManager->removeLsCacheBlock();
}
},
];
81 changes: 0 additions & 81 deletions src/Abstract/PurgeMiddleware.php

This file was deleted.

2 changes: 1 addition & 1 deletion src/Api/Controller/LSCacheCsrfResponseController.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

namespace ACPL\FlarumCache\Api\Controller;
namespace ACPL\FlarumLSCache\Api\Controller;

use Laminas\Diactoros\Response\EmptyResponse;
use Psr\Http\Message\ResponseInterface;
Expand Down
6 changes: 3 additions & 3 deletions src/Api/Controller/PurgeLSCacheController.php
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<?php

namespace ACPL\FlarumCache\Api\Controller;
namespace ACPL\FlarumLSCache\Api\Controller;

use ACPL\FlarumCache\LSCacheHeadersEnum;
use ACPL\FlarumLSCache\LSCacheHeader;
use Flarum\Http\RequestUtil;
use Flarum\Settings\SettingsRepositoryInterface;
use Flarum\User\Exception\PermissionDeniedException;
Expand Down Expand Up @@ -54,6 +54,6 @@ public function handle(ServerRequestInterface $request): ResponseInterface
$purgeStr .= '*';
}

return (new EmptyResponse())->withHeader(LSCacheHeadersEnum::PURGE, $purgeStr);
return (new EmptyResponse())->withHeader(LSCacheHeader::PURGE, $purgeStr);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

namespace ACPL\FlarumCache\Command;
namespace ACPL\FlarumLSCache\Command;

use Flarum\Api\ApiKey;
use Flarum\Console\AbstractCommand;
Expand All @@ -10,7 +10,7 @@
use GuzzleHttp\Exception\GuzzleException;
use Symfony\Component\Console\Input\InputOption;

class LSCacheClearCommand extends AbstractCommand
class LSCachePurgeCommand extends AbstractCommand
{
protected UrlGenerator $url;
private SettingsRepositoryInterface $settings;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php

namespace ACPL\FlarumLSCache\Compatibility\ClarkWinkelmann\AuthorChange;

use ACPL\FlarumLSCache\Listener\{AbstractCachePurgeSubscriber, DiscussionCachePurgeTrait};
use ClarkWinkelmann\AuthorChange\Event\{
DiscussionCreateDateChanged,
DiscussionUserChanged,
PostCreateDateChanged,
PostEditDateChanged,
PostUserChanged
};
use Illuminate\Contracts\Events\Dispatcher;

class ClarkWinkelmannAuthorChangeEventSubscriber extends AbstractCachePurgeSubscriber
{
use DiscussionCachePurgeTrait;

public function subscribe(Dispatcher $events): void
{
$this->addPurgeListener($events, DiscussionCreateDateChanged::class, [$this, 'handleDiscussion']);
$this->addPurgeListener($events, DiscussionUserChanged::class, [$this, 'handleDiscussionUserChanged']);
$this->addPurgeListener($events, PostCreateDateChanged::class, [$this, 'handlePost']);
$this->addPurgeListener($events, PostEditDateChanged::class, [$this, 'handlePost']);
$this->addPurgeListener($events, PostUserChanged::class, [$this, 'handlePostUserChanged']);
}

protected function handleDiscussion(DiscussionCreateDateChanged|DiscussionUserChanged $event): void
{
$this->handleDiscussionRelatedPurge();
$this->purger->addPurgeTags([
"discussion_{$event->discussion->id}",
"user_{$event->discussion->user->id}",
"user_{$event->discussion->user->username}",
]);
}

protected function handleDiscussionUserChanged(DiscussionUserChanged $event): void
{
$this->handleDiscussion($event);
$this->purger->addPurgeTags([
"user_{$event->oldUser->id}",
"user_{$event->oldUser->username}",
]);
}

protected function handlePost(PostCreateDateChanged|PostEditDateChanged|PostUserChanged $event): void
{
$this->purger->addPurgeTags([
"discussion_{$event->post->discussion->id}",
'posts.index',
"post_{$event->post->id}",
"user_{$event->post->user->id}",
"user_{$event->post->user->username}",
]);
}

protected function handlePostUserChanged(PostUserChanged $event): void
{
$this->handlePost($event);
$this->purger->addPurgeTags([
"user_{$event->oldUser->id}",
"user_{$event->oldUser->username}",
]);
}
}
Loading

0 comments on commit 41cd90d

Please sign in to comment.