diff --git a/activitypub.py b/activitypub.py index 65f9a71..b9c8cf2 100644 --- a/activitypub.py +++ b/activitypub.py @@ -911,7 +911,6 @@ def postprocess_as2_actor(actor, user): @app.get(f'/ap/web/') # special case Web users without /ap/web/ prefix, for backward compatibility @app.get(f'/') -@flask_util.cached(cache, CACHE_TIME) def actor(handle_or_id): """Serves a user's AS2 actor from the datastore.""" if handle_or_id == PRIMARY_DOMAIN or handle_or_id in PROTOCOL_DOMAINS: @@ -971,10 +970,11 @@ def actor(handle_or_id): # 'alsoKnownAs': [host_url(id)], }) - logger.info(f'Returning: {json_dumps(actor, indent=2)}') + # logger.info(f'Returning: {json_dumps(actor, indent=2)}') return actor, { 'Content-Type': as2.CONTENT_TYPE_LD_PROFILE, 'Access-Control-Allow-Origin': '*', + 'Cache-Control': f'public, max-age={CACHE_TIME.total_seconds()}' } @@ -1057,7 +1057,6 @@ def inbox(protocol=None, id=None): # special case Web users without /ap/web/ prefix, for backward compatibility @app.route(f'//', methods=['GET', 'HEAD']) -@flask_util.cached(cache, CACHE_TIME) def follower_collection(id, collection): """ActivityPub Followers and Following collections. @@ -1100,7 +1099,7 @@ def follower_collection(id, collection): '@context': 'https://www.w3.org/ns/activitystreams', 'id': request.url, }) - logger.info(f'Returning {json_dumps(page, indent=2)}') + # logger.info(f'Returning {json_dumps(page, indent=2)}') return page, {'Content-Type': as2.CONTENT_TYPE_LD_PROFILE} # collection @@ -1113,8 +1112,11 @@ def follower_collection(id, collection): 'totalItems': num_followers if collection == 'followers' else num_following, 'first': page, } - logger.info(f'Returning {json_dumps(collection, indent=2)}') - return collection, {'Content-Type': as2.CONTENT_TYPE_LD_PROFILE} + # logger.info(f'Returning {json_dumps(collection, indent=2)}') + return collection, { + 'Content-Type': as2.CONTENT_TYPE_LD_PROFILE, + 'Cache-Control': f'public, max-age={CACHE_TIME.total_seconds()}' + } # protocol in subdomain @@ -1123,7 +1125,6 @@ def follower_collection(id, collection): @app.get(f'/ap/web//outbox') # special case Web users without /ap/web/ prefix, for backward compatibility @app.route(f'//outbox', methods=['GET', 'HEAD']) -@flask_util.cached(cache, CACHE_TIME) def outbox(id): """Serves a user's AP outbox. @@ -1161,7 +1162,7 @@ def outbox(id): '@context': 'https://www.w3.org/ns/activitystreams', 'id': request.url, }) - logger.info(f'Returning {json_dumps(page, indent=2)}') + # logger.info(f'Returning {json_dumps(page, indent=2)}') return page, {'Content-Type': as2.CONTENT_TYPE_LD_PROFILE} # collection @@ -1172,4 +1173,7 @@ def outbox(id): 'summary': f"{id}'s outbox", 'totalItems': query.count(), 'first': page, - }, {'Content-Type': as2.CONTENT_TYPE_LD_PROFILE} + }, { + 'Content-Type': as2.CONTENT_TYPE_LD_PROFILE, + 'Cache-Control': f'public, max-age={CACHE_TIME.total_seconds()}' + } diff --git a/common.py b/common.py index 19b65db..1eaff6d 100644 --- a/common.py +++ b/common.py @@ -73,7 +73,7 @@ SMTP_PORT = 587 SUBDOMAIN_BASE_URL_RE = None ID_FIELDS = ['id', 'object', 'actor', 'author', 'inReplyTo', 'url'] -CACHE_TIME = timedelta(seconds=60) +CACHE_TIME = timedelta(hours=1) USER_AGENT = 'Bridgy Fed (https://fed.brid.gy/)' util.set_user_agent(USER_AGENT) diff --git a/convert.py b/convert.py index 1f0773f..42bb567 100644 --- a/convert.py +++ b/convert.py @@ -23,7 +23,6 @@ logger = logging.getLogger(__name__) @app.get(f'/convert//') -@flask_util.cached(cache, CACHE_TIME, headers=['Accept']) def convert(dest, _, src=None): """Converts data from one protocol to another and serves it. @@ -77,12 +76,18 @@ def convert(dest, _, src=None): logger.info(f'{type} activity, redirecting to Object {obj_id}') return redirect(f'/{path_prefix}{obj_id}', code=301) + headers = { + 'Content-Type': dest_cls.CONTENT_TYPE, + 'Vary': 'Accept', + 'Cache-Control': f'public, max-age={CACHE_TIME.total_seconds()}' + } + # don't serve deletes or deleted objects if obj.deleted or type == 'delete': - return '', 410 + return '', 410, headers # convert and serve - return dest_cls.convert(obj), {'Content-Type': dest_cls.CONTENT_TYPE} + return dest_cls.convert(obj), headers @app.get('/render') diff --git a/pages.py b/pages.py index ac64f0c..cd1405e 100644 --- a/pages.py +++ b/pages.py @@ -24,8 +24,8 @@ import werkzeug.exceptions from activitypub import ActivityPub, instance_actor import common -from common import DOMAIN_RE -from flask_app import app, cache +from common import CACHE_TIME, DOMAIN_RE +from flask_app import app import ids from models import fetch_objects, fetch_page, Follower, Object, PAGE_SIZE, PROTOCOLS from protocol import Protocol @@ -96,18 +96,20 @@ def load_user(protocol, id): @app.route('/') @canonicalize_request_domain(common.PROTOCOL_DOMAINS, common.PRIMARY_DOMAIN) -@flask_util.cached(cache, datetime.timedelta(days=1)) def front_page(): """View for the front page.""" - return render_template('index.html') + return render_template('index.html'), { + 'Cache-Control': f'public, max-age={CACHE_TIME.total_seconds()}' + } @app.route('/docs') @canonicalize_request_domain(common.PROTOCOL_DOMAINS, common.PRIMARY_DOMAIN) -@flask_util.cached(cache, datetime.timedelta(days=1)) def docs(): """View for the docs page.""" - return render_template('docs.html') + return render_template('docs.html'), { + 'Cache-Control': f'public, max-age={CACHE_TIME.total_seconds()}' + } @app.get(f'/user/') @@ -344,7 +346,6 @@ def stats(): @app.get('/.well-known/nodeinfo') @canonicalize_request_domain(common.PROTOCOL_DOMAINS, common.PRIMARY_DOMAIN) -@flask_util.cached(cache, datetime.timedelta(days=1)) def nodeinfo_jrd(): """ https://nodeinfo.diaspora.software/protocol.html @@ -359,12 +360,12 @@ def nodeinfo_jrd(): }], }, { 'Content-Type': 'application/jrd+json', + 'Cache-Control': f'public, max-age={CACHE_TIME.total_seconds()}' } @app.get('/nodeinfo.json') @canonicalize_request_domain(common.PROTOCOL_DOMAINS, common.PRIMARY_DOMAIN) -@flask_util.cached(cache, datetime.timedelta(days=1)) def nodeinfo(): """ https://nodeinfo.diaspora.software/schema.html @@ -410,12 +411,12 @@ def nodeinfo(): }, { # https://nodeinfo.diaspora.software/protocol.html 'Content-Type': 'application/json; profile="http://nodeinfo.diaspora.software/ns/schema/2.1#"', + 'Cache-Control': f'public, max-age={datetime.timedelta(days=1).total_seconds()}' } @app.get('/api/v1/instance') @canonicalize_request_domain(common.PROTOCOL_DOMAINS, common.PRIMARY_DOMAIN) -@flask_util.cached(cache, datetime.timedelta(days=1)) def instance_info(): """ https://docs.joinmastodon.org/methods/instance/#v1 @@ -437,11 +438,14 @@ def instance_info(): 'display_name': 'Ryan', 'url': 'https://snarfed.org/', }, + }, { + 'Cache-Control': f'public, max-age={CACHE_TIME.total_seconds()}' } @app.get('/log') @canonicalize_request_domain(common.PROTOCOL_DOMAINS, common.PRIMARY_DOMAIN) -@flask_util.cached(cache, logs.CACHE_TIME) def log(): - return logs.log() + return logs.log(), { + 'Cache-Control': f'public, max-age={CACHE_TIME.total_seconds()}' + } diff --git a/redirect.py b/redirect.py index 3604753..42a94d3 100644 --- a/redirect.py +++ b/redirect.py @@ -46,7 +46,6 @@ DOMAIN_ALLOWLIST = frozenset(( VARY_HEADER = {'Vary': 'Accept'} @app.get(r'/r/') -@flask_util.cached(cache, CACHE_TIME, headers=['Accept']) def redir(to): """Either redirect to a given URL or convert it to another format. @@ -133,7 +132,7 @@ def redir(to): return f'Object not found: {to}', 404, VARY_HEADER ret = ActivityPub.convert(obj, from_user=web_user) - logger.info(f'Returning: {json_dumps(ret, indent=2)}') + # logger.info(f'Returning: {json_dumps(ret, indent=2)}') return ret, { 'Content-Type': (as2.CONTENT_TYPE_LD_PROFILE if accept_type == as2.CONTENT_TYPE_LD diff --git a/web.py b/web.py index 138464c..4b1a964 100644 --- a/web.py +++ b/web.py @@ -24,7 +24,13 @@ from requests.auth import HTTPBasicAuth from werkzeug.exceptions import BadGateway, BadRequest, HTTPException, NotFound import common -from common import add, DOMAIN_RE, PRIMARY_DOMAIN, PROTOCOL_DOMAINS, SUPERDOMAIN +from common import ( + CACHE_TIME, + DOMAIN_RE, + PRIMARY_DOMAIN, + PROTOCOL_DOMAINS, + SUPERDOMAIN, +) from flask_app import app, cache from ids import translate_object_id, translate_user_id from models import Follower, Object, PROTOCOLS, Target, User @@ -574,9 +580,10 @@ class Web(User, Protocol): @app.get('/web-site') -@flask_util.cached(cache, timedelta(days=1)) def enter_web_site(): - return render_template('enter_web_site.html') + return render_template('enter_web_site.html'), { + 'Cache-Control': f'public, max-age={CACHE_TIME.total_seconds()}' + } @app.post('/web-site') @@ -712,7 +719,7 @@ def poll_feed_task(): if url := elem.get('url'): elem['id'] = elem['url'] - logger.info(f'Converted to AS1: {json_dumps(activity, indent=2)}') + # logger.info(f'Converted to AS1: {json_dumps(activity, indent=2)}') id = Object(our_as1=activity).as1.get('id') if not id: diff --git a/webfinger.py b/webfinger.py index b08fd0f..c8552ac 100644 --- a/webfinger.py +++ b/webfinger.py @@ -3,6 +3,7 @@ * https://webfinger.net/ * https://tools.ietf.org/html/rfc7033 """ +from datetime import timedelta import logging from urllib.parse import urljoin, urlparse @@ -14,7 +15,13 @@ from oauth_dropins.webutil.util import json_dumps, json_loads import activitypub import common -from common import LOCAL_DOMAINS, PRIMARY_DOMAIN, PROTOCOL_DOMAINS, SUPERDOMAIN +from common import ( + CACHE_TIME, + LOCAL_DOMAINS, + PRIMARY_DOMAIN, + PROTOCOL_DOMAINS, + SUPERDOMAIN, +) from flask_app import app, cache from protocol import Protocol from web import Web @@ -30,9 +37,10 @@ class Webfinger(flask_util.XrdOrJrd): Supports both JRD and XRD; defaults to JRD. https://tools.ietf.org/html/rfc7033#section-4 """ - @flask_util.cached(cache, common.CACHE_TIME, headers=['Accept']) def dispatch_request(self, *args, **kwargs): - return super().dispatch_request(*args, **kwargs) + body, headers = super().dispatch_request(*args, **kwargs) + headers['Cache-Control'] = f'public, max-age={CACHE_TIME.total_seconds()}' + return body, headers def template_prefix(self): return 'webfinger_user' @@ -204,8 +212,10 @@ class HostMeta(flask_util.XrdOrJrd): @app.get('/.well-known/host-meta.xrds') def host_meta_xrds(): """Renders and serves the ``/.well-known/host-meta.xrds`` XRDS-Simple file.""" - return (render_template('host-meta.xrds', host_uri=common.host_url()), - {'Content-Type': 'application/xrds+xml'}) + return render_template('host-meta.xrds', host_uri=common.host_url()), { + 'Content-Type': 'application/xrds+xml', + 'Cache-Control': f'public, max-age={CACHE_TIME.total_seconds()}' + } def fetch(addr):