From b8460b0acd3e833b9ec302d9fa3fb6ab28032c0e Mon Sep 17 00:00:00 2001 From: Michael Manfre Date: Tue, 6 Dec 2022 00:23:07 -0500 Subject: Only cache unauthenticated page views (#117) --- activities/views/follows.py | 4 ---- activities/views/posts.py | 6 ++++-- activities/views/timelines.py | 10 +++------ core/decorators.py | 50 +++++++++++++++++++++++++++++++++++++------ core/views.py | 2 +- users/views/identity.py | 6 +++--- 6 files changed, 55 insertions(+), 23 deletions(-) diff --git a/activities/views/follows.py b/activities/views/follows.py index 841c8cc..44d8adc 100644 --- a/activities/views/follows.py +++ b/activities/views/follows.py @@ -1,15 +1,11 @@ from django.utils.decorators import method_decorator from django.views.generic import TemplateView -from core.decorators import per_identity_cache_page from users.decorators import identity_required from users.models import FollowStates @method_decorator(identity_required, name="dispatch") -@method_decorator( - per_identity_cache_page("cache_timeout_page_timeline"), name="dispatch" -) class FollowsPage(TemplateView): """ Shows followers/follows. diff --git a/activities/views/posts.py b/activities/views/posts.py index adc732c..d343567 100644 --- a/activities/views/posts.py +++ b/activities/views/posts.py @@ -6,13 +6,15 @@ from django.utils.decorators import method_decorator from django.views.generic import TemplateView, View from activities.models import Post, PostInteraction, PostInteractionStates, PostStates -from core.decorators import per_identity_cache_page +from core.decorators import cache_page_by_ap_json from core.ld import canonicalise from users.decorators import identity_required from users.shortcuts import by_handle_or_404 -@method_decorator(per_identity_cache_page("cache_timeout_page_post"), name="dispatch") +@method_decorator( + cache_page_by_ap_json("cache_timeout_page_post", public_only=True), name="dispatch" +) class Individual(TemplateView): template_name = "activities/post.html" diff --git a/activities/views/timelines.py b/activities/views/timelines.py index 2b456ad..711c357 100644 --- a/activities/views/timelines.py +++ b/activities/views/timelines.py @@ -5,13 +5,12 @@ from django.utils.decorators import method_decorator from django.views.generic import FormView, ListView from activities.models import Hashtag, Post, PostInteraction, TimelineEvent -from core.decorators import per_identity_cache_page +from core.decorators import cache_page from core.models import Config from users.decorators import identity_required @method_decorator(identity_required, name="dispatch") -@method_decorator(per_identity_cache_page(), name="dispatch") class Home(FormView): template_name = "activities/home.html" @@ -64,7 +63,7 @@ class Home(FormView): @method_decorator( - per_identity_cache_page("cache_timeout_page_timeline"), name="dispatch" + cache_page("cache_timeout_page_timeline", public_only=True), name="dispatch" ) class Tag(ListView): @@ -102,7 +101,7 @@ class Tag(ListView): @method_decorator( - per_identity_cache_page("cache_timeout_page_timeline"), name="dispatch" + cache_page("cache_timeout_page_timeline", public_only=True), name="dispatch" ) class Local(ListView): @@ -130,9 +129,6 @@ class Local(ListView): @method_decorator(identity_required, name="dispatch") -@method_decorator( - per_identity_cache_page("cache_timeout_page_timeline"), name="dispatch" -) class Federated(ListView): template_name = "activities/federated.html" diff --git a/core/decorators.py b/core/decorators.py index fece564..dc8d4d2 100644 --- a/core/decorators.py +++ b/core/decorators.py @@ -1,34 +1,72 @@ +from collections.abc import Callable from functools import partial, wraps +from typing import ParamSpecArgs, ParamSpecKwargs +from django.http import HttpRequest from django.views.decorators.cache import cache_page as dj_cache_page from core.models import Config +VaryByFunc = Callable[[HttpRequest, ParamSpecArgs, ParamSpecKwargs], str] + + +def vary_by_ap_json(request, *args, **kwargs) -> str: + """ + Return a cache usable string token that is different based upon Accept + header. + """ + if request.ap_json: + return "ap_json" + return "not_ap" + + +def vary_by_identity(request, *args, **kwargs) -> str: + """ + Return a cache usable string token that is different based upon the + request.identity + """ + if request.identity: + return f"ident{request.identity.pk}" + return "identNone" + def cache_page( timeout: int | str = "cache_timeout_page_default", *, - per_identity: bool = False, key_prefix: str = "", + public_only: bool = False, + vary_by: VaryByFunc | list[VaryByFunc] | None = None, ): """ Decorator for views that caches the page result. timeout can either be the number of seconds or the name of a SystemOptions value. + If public_only is True, requests with an identity are not cached. """ _timeout = timeout + _prefix = key_prefix + if callable(vary_by): + vary_by = [vary_by] def decorator(function): @wraps(function) def inner(request, *args, **kwargs): - prefix = key_prefix - if per_identity: - identity_id = request.identity.pk if request.identity else "0" - prefix = f"{key_prefix or ''}:ident{identity_id}" + if public_only: + if request.user.is_authenticated: + return function(request, *args, **kwargs) + + prefix = [_prefix] + + if isinstance(vary_by, list): + prefix.extend([vfunc(request, *args, **kwargs) for vfunc in vary_by]) + + prefix = "".join(prefix) + if isinstance(_timeout, str): timeout = getattr(Config.system, _timeout) else: timeout = _timeout + return dj_cache_page(timeout=timeout, key_prefix=prefix)(function)( request, *args, **kwargs ) @@ -38,4 +76,4 @@ def cache_page( return decorator -per_identity_cache_page = partial(cache_page, per_identity=True) +cache_page_by_ap_json = partial(cache_page, vary_by=[vary_by_ap_json]) diff --git a/core/views.py b/core/views.py index a09d925..2772eb8 100644 --- a/core/views.py +++ b/core/views.py @@ -18,7 +18,7 @@ def homepage(request): return LoggedOutHomepage.as_view()(request) -@method_decorator(cache_page(), name="dispatch") +@method_decorator(cache_page(public_only=True), name="dispatch") class LoggedOutHomepage(TemplateView): template_name = "index.html" diff --git a/users/views/identity.py b/users/views/identity.py index 4996ad7..4ca230a 100644 --- a/users/views/identity.py +++ b/users/views/identity.py @@ -10,7 +10,7 @@ from django.utils.decorators import method_decorator from django.views.generic import FormView, ListView, TemplateView, View from activities.models import Post, PostInteraction -from core.decorators import per_identity_cache_page +from core.decorators import cache_page, cache_page_by_ap_json from core.ld import canonicalise from core.models import Config from users.decorators import identity_required @@ -18,7 +18,7 @@ from users.models import Domain, Follow, FollowStates, Identity, IdentityStates from users.shortcuts import by_handle_or_404 -@method_decorator(per_identity_cache_page(), name="dispatch") +@method_decorator(cache_page_by_ap_json(public_only=True), name="dispatch") class ViewIdentity(ListView): """ Shows identity profile pages, and also acts as the Actor endpoint when @@ -88,7 +88,7 @@ class ViewIdentity(ListView): @method_decorator( - per_identity_cache_page("cache_timeout_identity_feed"), name="__call__" + cache_page("cache_timeout_identity_feed", public_only=True), name="__call__" ) class IdentityFeed(Feed): """ -- cgit v1.2.3