Unverified Commit 2d297f45 authored by Yassine Doghri's avatar Yassine Doghri
Browse files

feat: add cache to ActivityPub sql queries + cache activity and note pages

- authenticated pages are not cached
- add AnalyticsTrait to register a podcast webpage hit across
mutliple controllers
- set actor_id as unique in podcasts table
- fix issues with preview card not appearing
- update codeigniter4-uuid
parent 54b84f96
Loading
Loading
Loading
Loading
+73 −1
Original line number Diff line number Diff line
@@ -81,6 +81,10 @@ Events::on('on_note_add', function ($note) {
            ->where('id', $note->episode_id)
            ->increment('notes_total');
    }

    // Removing all of the podcast pages is a bit overkill, but works perfectly
    // same for other events below
    cache()->deleteMatching("page_podcast#{$note->actor->podcast->id}*");
});

Events::on('on_note_remove', function ($note) {
@@ -97,6 +101,9 @@ Events::on('on_note_remove', function ($note) {
            ->where('id', $note->episode_id)
            ->decrement('favourites_total', $note->favourites_count);
    }

    cache()->deleteMatching("page_podcast#{$note->actor->podcast->id}*");
    cache()->deleteMatching("page_note#{$note->id}*");
});

Events::on('on_note_reblog', function ($actor, $note) {
@@ -109,10 +116,18 @@ Events::on('on_note_reblog', function ($actor, $note) {
            ->where('id', $episodeId)
            ->increment('notes_total');
    }

    cache()->deleteMatching("page_podcast#{$note->actor->podcast->id}*");
    cache()->deleteMatching("page_note#{$note->id}*");

    if ($actor->is_podcast) {
        cache()->deleteMatching("page_podcast#{$actor->podcast->id}*");
    }
});

Events::on('on_note_undo_reblog', function ($reblogNote) {
    if ($episodeId = $reblogNote->reblog_of_note->episode_id) {
    $note = $reblogNote->reblog_of_note;
    if ($episodeId = $note->episode_id) {
        model('EpisodeModel')
            ->where('id', $episodeId)
            ->decrement('reblogs_total');
@@ -121,6 +136,29 @@ Events::on('on_note_undo_reblog', function ($reblogNote) {
            ->where('id', $episodeId)
            ->decrement('notes_total');
    }

    cache()->deleteMatching("page_podcast#{$note->actor->podcast->id}*");
    cache()->deleteMatching("page_note#{$note->id}*");

    if ($reblogNote->actor->is_podcast) {
        cache()->deleteMatching(
            "page_podcast#{$reblogNote->actor->podcast->id}*",
        );
    }
});

Events::on('on_note_reply', function ($reply) {
    $note = $reply->reply_to_note;

    cache()->deleteMatching("page_podcast#{$note->actor->podcast->id}*");
    cache()->deleteMatching("page_note#{$note->id}*");
});

Events::on('on_reply_remove', function ($reply) {
    $note = $reply->reply_to_note;

    cache()->deleteMatching("page_podcast#{$note->actor->podcast->id}*");
    cache()->deleteMatching("page_note#{$note->id}*");
});

Events::on('on_note_favourite', function ($actor, $note) {
@@ -129,6 +167,13 @@ Events::on('on_note_favourite', function ($actor, $note) {
            ->where('id', $note->episode_id)
            ->increment('favourites_total');
    }

    cache()->deleteMatching("page_podcast#{$actor->podcast->id}*");
    cache()->deleteMatching("page_note#{$note->id}*");

    if ($note->in_reply_to_id) {
        cache()->deleteMatching("page_note#{$note->in_reply_to_id}*");
    }
});

Events::on('on_note_undo_favourite', function ($actor, $note) {
@@ -137,4 +182,31 @@ Events::on('on_note_undo_favourite', function ($actor, $note) {
            ->where('id', $note->episode_id)
            ->decrement('favourites_total');
    }

    cache()->deleteMatching("page_podcast#{$actor->podcast->id}*");
    cache()->deleteMatching("page_note#{$note->id}*");

    if ($note->in_reply_to_id) {
        cache()->deleteMatching("page_note#{$note->in_reply_to_id}*");
    }
});

Events::on('on_block_actor', function ($actorId) {
    cache()->deleteMatching('page_podcast*');
    cache()->deleteMatching('page_note*');
});

Events::on('on_unblock_actor', function ($actorId) {
    cache()->deleteMatching('page_podcast*');
    cache()->deleteMatching('page_note*');
});

Events::on('on_block_domain', function ($domainName) {
    cache()->deleteMatching('page_podcast*');
    cache()->deleteMatching('page_note*');
});

Events::on('on_unblock_domain', function ($domainName) {
    cache()->deleteMatching('page_podcast*');
    cache()->deleteMatching('page_note*');
});
+22 −5
Original line number Diff line number Diff line
@@ -8,15 +8,32 @@

namespace App\Controllers;

use Analytics\AnalyticsTrait;

class Actor extends \ActivityPub\Controllers\ActorController
{
    use AnalyticsTrait;

    public function follow()
    {
        // Prevent analytics hit when authenticated
        if (!can_user_interact()) {
            $this->registerPodcastWebpageHit($this->actor->podcast->id);
        }

        $cacheName = "page_podcast@{$this->actor->username}_follow";
        if (!($cachedView = cache($cacheName))) {
            helper(['form', 'components', 'svg']);
            $data = [
                'actor' => $this->actor,
            ];

        return view('podcast/follow', $data);
            return view('podcast/follow', $data, [
                'cache' => DECADE,
                'cache_name' => $cacheName,
            ]);
        }

        return $cachedView;
    }
}
+1 −11
Original line number Diff line number Diff line
@@ -26,7 +26,7 @@ class BaseController extends Controller
     *
     * @var array
     */
    protected $helpers = ['auth', 'analytics', 'svg', 'components', 'misc'];
    protected $helpers = ['auth', 'svg', 'components', 'misc'];

    /**
     * Constructor.
@@ -47,15 +47,5 @@ class BaseController extends Controller
        // Preload any models, libraries, etc, here.
        //--------------------------------------------------------------------
        // E.g.: $this->session = \Config\Services::session();

        set_user_session_deny_list_ip();
        set_user_session_browser();
        set_user_session_referer();
        set_user_session_entry_page();
    }

    protected static function triggerWebpageHit($podcastId)
    {
        webpage_hit($podcastId);
    }
}
+16 −11
Original line number Diff line number Diff line
@@ -8,12 +8,15 @@

namespace App\Controllers;

use Analytics\AnalyticsTrait;
use App\Models\EpisodeModel;
use App\Models\PodcastModel;
use SimpleXMLElement;

class Episode extends BaseController
{
    use AnalyticsTrait;

    /**
     * @var \App\Entities\Podcast
     */
@@ -44,10 +47,15 @@ class Episode extends BaseController

    public function index()
    {
        self::triggerWebpageHit($this->podcast->id);
        // Prevent analytics hit when authenticated
        if (!can_user_interact()) {
            $this->registerPodcastWebpageHit($this->episode->podcast_id);
        }

        $locale = service('request')->getLocale();
        $cacheName = "page_podcast#{$this->podcast->id}_episode{$this->episode->id}_{$locale}";
        $cacheName =
            "page_podcast#{$this->podcast->id}_episode#{$this->episode->id}_{$locale}" .
            (can_user_interact() ? '_authenticated' : '');

        if (!($cachedView = cache($cacheName))) {
            helper('persons');
@@ -69,13 +77,7 @@ class Episode extends BaseController

            if (can_user_interact()) {
                helper('form');
                // The page cache is set to a decade so it is deleted manually upon podcast update
                return view('podcast/episode_authenticated', $data, [
                    'cache' => $secondsToNextUnpublishedEpisode
                        ? $secondsToNextUnpublishedEpisode
                        : DECADE,
                    'cache_name' => $cacheName . '_authenticated',
                ]);
                return view('podcast/episode_authenticated', $data);
            } else {
                // The page cache is set to a decade so it is deleted manually upon podcast update
                return view('podcast/episode', $data, [
@@ -94,7 +96,10 @@ class Episode extends BaseController
    {
        header('Content-Security-Policy: frame-ancestors https://* http://*');

        self::triggerWebpageHit($this->episode->podcast_id);
        // Prevent analytics hit when authenticated
        if (!can_user_interact()) {
            $this->registerPodcastWebpageHit($this->episode->podcast_id);
        }

        $session = \Config\Services::session();
        $session->start();
@@ -107,7 +112,7 @@ class Episode extends BaseController

        $locale = service('request')->getLocale();

        $cacheName = "page_podcast#{$this->podcast->id}_episode{$this->episode->id}_embeddable_player_{$theme}_{$locale}";
        $cacheName = "page_podcast#{$this->podcast->id}_episode#{$this->episode->id}_embeddable_player_{$theme}_{$locale}";

        if (!($cachedView = cache($cacheName))) {
            $theme = EpisodeModel::$themes[$theme];
+71 −24
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@

namespace App\Controllers;

use Analytics\AnalyticsTrait;
use App\Models\EpisodeModel;
use App\Models\PodcastModel;
use CodeIgniter\HTTP\URI;
@@ -15,6 +16,8 @@ use CodeIgniter\I18n\Time;

class Note extends \ActivityPub\Controllers\NoteController
{
    use AnalyticsTrait;

    /**
     * @var \App\Entities\Podcast
     */
@@ -47,6 +50,22 @@ class Note extends \ActivityPub\Controllers\NoteController

    public function index()
    {
        // Prevent analytics hit when authenticated
        if (!can_user_interact()) {
            $this->registerPodcastWebpageHit($this->podcast->id);
        }

        $cacheName = implode(
            '_',
            array_filter([
                'page',
                "note#{$this->note->id}",
                service('request')->getLocale(),
                can_user_interact() ? '_authenticated' : null,
            ]),
        );

        if (!($cachedView = cache($cacheName))) {
            helper('persons');
            $persons = [];
            construct_person_array($this->podcast->persons, $persons);
@@ -63,10 +82,16 @@ class Note extends \ActivityPub\Controllers\NoteController
                helper('form');
                return view('podcast/note_authenticated', $data);
            } else {
            return view('podcast/note', $data);
                return view('podcast/note', $data, [
                    'cache' => DECADE,
                    'cache_name' => $cacheName,
                ]);
            }
        }

        return $cachedView;
    }

    public function attemptCreate()
    {
        $rules = [
@@ -198,6 +223,22 @@ class Note extends \ActivityPub\Controllers\NoteController

    public function remoteAction($action)
    {
        // Prevent analytics hit when authenticated
        if (!can_user_interact()) {
            $this->registerPodcastWebpageHit($this->podcast->id);
        }

        $cacheName = implode(
            '_',
            array_filter([
                'page',
                "note#{$this->note->id}",
                "remote_{$action}",
                service('request')->getLocale(),
            ]),
        );

        if (!($cachedView = cache($cacheName))) {
            $data = [
                'podcast' => $this->podcast,
                'actor' => $this->actor,
@@ -207,6 +248,12 @@ class Note extends \ActivityPub\Controllers\NoteController

            helper('form');

        return view('podcast/note_remote_action', $data);
            return view('podcast/note_remote_action', $data, [
                'cache' => DECADE,
                'cache_name' => $cacheName,
            ]);
        }

        return $cachedView;
    }
}
Loading