-
Notifications
You must be signed in to change notification settings - Fork 81
Support for WP REST Cache #1630
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
979099b
Support for WP REST Cache
obenland a42c9ca
Merge branch 'trunk' into add/wp-rest-cache-support
pfefferle ba4f387
Second pass at Rest Cache integration
obenland 30f9634
Trailing period.
obenland 2553641
Merge branch 'trunk' into add/wp-rest-cache-support
obenland 7811e34
Add changelog
matticbot 2d3a51a
Update integration/class-wp-rest-cache.php
obenland fc4b737
Apply suggestions from code review
obenland d19c907
Merge branch 'trunk' into add/wp-rest-cache-support
obenland 155ba46
Apply suggestions from code review
obenland daef91c
Merge branch 'trunk' into add/wp-rest-cache-support
obenland File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
Significance: minor | ||
Type: added | ||
|
||
Added support for the WP Rest Cache plugin to help with caching REST API responses. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
<?php | ||
/** | ||
* WP REST Cache integration file. | ||
* | ||
* This file contains code for caching ActivityPub REST requests. | ||
* | ||
* Copyright (C) 2025 Epiphyt | ||
* Original code: https://epiph.yt/en/blog/2025/accidental-ddos-through-activitypub-plugin/ | ||
* | ||
* Portions of this code are adapted from GPL v2 licensed code. | ||
* As such, you may also redistribute and/or modify those portions under the terms of | ||
* the GNU General Public License as published by the Free Software Foundation. | ||
* | ||
* https://www.gnu.org/licenses/gpl-2.0.html | ||
* | ||
* @package Activitypub | ||
*/ | ||
|
||
namespace Activitypub\Integration; | ||
|
||
use Activitypub\Collection\Outbox; | ||
use Activitypub\Comment; | ||
use WP_Rest_Cache_Plugin\Includes\Caching\Caching; | ||
|
||
/** | ||
* Compatibility with the WP REST Cache plugin. | ||
* | ||
* @see https://wordpress.org/plugins/wp-rest-cache/ | ||
*/ | ||
class WP_Rest_Cache { | ||
/** | ||
* Initialize the class, registering WordPress hooks. | ||
*/ | ||
public static function init() { | ||
\add_filter( 'wp_rest_cache/allowed_endpoints', array( self::class, 'add_activitypub_endpoints' ) ); | ||
\add_filter( 'wp_rest_cache/determine_object_type', array( self::class, 'set_object_type' ), 10, 4 ); | ||
\add_filter( 'wp_rest_cache/is_single_item', array( self::class, 'set_is_single_item' ), 10, 3 ); | ||
\add_action( 'transition_post_status', array( self::class, 'transition_post_status' ), 10, 3 ); | ||
\add_action( 'transition_comment_status', array( self::class, 'transition_comment_status' ), 10, 3 ); | ||
} | ||
|
||
/** | ||
* Add ActivityPub endpoints to the list of allowed endpoints. | ||
* | ||
* @param array $endpoints List of allowed endpoints. | ||
* | ||
* @return array Filtered list of allowed endpoints. | ||
*/ | ||
public static function add_activitypub_endpoints( $endpoints ) { | ||
$endpoints[ ACTIVITYPUB_REST_NAMESPACE ] = array( 'actors', 'collections', 'comments', 'interactions', 'nodeinfo', 'posts', 'users' ); | ||
|
||
return $endpoints; | ||
} | ||
|
||
/** | ||
* Set whether the cache represents a single item. | ||
* | ||
* Always return false for ActivityPub endpoints, since cache entries cannot be flushed otherwise. | ||
* | ||
* @param bool $is_single Whether the current cache represents a single item. | ||
* @param mixed $data Data to cache. | ||
* @param string $uri Request URI. | ||
* | ||
* @return bool Whether the cache represents a single item. | ||
*/ | ||
public static function set_is_single_item( $is_single, $data, $uri ) { | ||
if ( self::is_activitypub_endpoint( $uri ) ) { | ||
return false; | ||
} | ||
|
||
return $is_single; | ||
} | ||
|
||
/** | ||
* Set object type for ActivityPub. | ||
* | ||
* @param string $object_type Object type. | ||
* @param string $cache_key Object key. | ||
* @param mixed $data Data to cache. | ||
* @param string $uri Request URI. | ||
* | ||
* @return string Updated object type. | ||
*/ | ||
public static function set_object_type( $object_type, $cache_key, $data, $uri ) { | ||
if ( self::is_activitypub_endpoint( $uri ) ) { | ||
return 'ActivityPub'; | ||
} | ||
|
||
return $object_type; | ||
} | ||
|
||
/** | ||
* Reset cache by transition post status. | ||
* | ||
* @param string $new_status New post status. | ||
* @param string $old_status Old post status. | ||
* @param \WP_Post $post Post object. | ||
*/ | ||
public static function transition_post_status( $new_status, $old_status, $post ) { | ||
if ( 'publish' !== $new_status && 'publish' !== $old_status ) { | ||
return; | ||
} | ||
|
||
$post_types = (array) \get_option( 'activitypub_support_post_types', array() ); | ||
$post_types[] = Outbox::POST_TYPE; | ||
|
||
if ( ! \in_array( $post->post_type, $post_types, true ) ) { | ||
return; | ||
} | ||
|
||
Caching::get_instance()->delete_object_type_caches( 'ActivityPub' ); | ||
} | ||
|
||
/** | ||
* Reset cache by transition comment status. | ||
* | ||
* @param string $new_status The new comment status. | ||
* @param string $old_status The old comment status. | ||
* @param \WP_Comment $comment Comment object. | ||
*/ | ||
public static function transition_comment_status( $new_status, $old_status, $comment ) { | ||
if ( 'approved' !== $new_status && 'approved' !== $old_status ) { | ||
return; | ||
} | ||
|
||
$comment_types = Comment::get_comment_type_slugs(); | ||
$comment_types[] = 'comment'; | ||
|
||
if ( ! \in_array( $comment->comment_type ?: 'comment', $comment_types, true ) ) { // phpcs:ignore Universal.Operators.DisallowShortTernary | ||
return; | ||
} | ||
|
||
Caching::get_instance()->delete_object_type_caches( 'ActivityPub' ); | ||
} | ||
|
||
/** | ||
* Test, whether the current endpoint is an ActivityPub endpoint. | ||
* | ||
* @param string $uri URI to test. | ||
* | ||
* @return bool Whether the current endpoint is an ActivityPub endpoint. | ||
*/ | ||
private static function is_activitypub_endpoint( $uri ) { | ||
$search = '/' . ACTIVITYPUB_REST_NAMESPACE . '/'; | ||
|
||
return \str_contains( $uri, $search ) || \str_contains( $uri, 'rest_route=' . \rawurlencode( $search ) ); | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isn't the namespace simply
activitypub
oractivitypub/1.0
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes,
activitypub/1.0
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I meant, can we wildcard all the subpathes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
with this specific list, we would have to take care that every new endpoint will also be added here... that is a bit fragile!?!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah! Yeah, unfortunately the plugin is pretty literal in its allowlist processing:
https://github.com/RedCastor/wp-rest-cache/blob/c6f94c7b61f30770424fa5dd5f4ccd4e0d7f52b9/includes/api/class-endpoint-api.php#L241-L251
We could grab all of our registered routes and format them to fit this list, but it would be like using a sledgehammer to crack a nut. I'm not too concerned, our endpoint routes have been pretty stable and chances are that any new endpoints are likely to be sub-paths of
actors
,users
, etc