mirror of
https://git.sr.ht/~tsileo/microblog.pub
synced 2024-11-15 03:04:28 +00:00
Cleanup, add helper for external caching
This commit is contained in:
parent
67643482c8
commit
56ad72148b
4 changed files with 44 additions and 13 deletions
|
@ -81,9 +81,8 @@ def _get_actor_id(actor: ObjectOrIDType) -> str:
|
||||||
|
|
||||||
|
|
||||||
class BaseActivity(object):
|
class BaseActivity(object):
|
||||||
ACTIVITY_TYPE = None # type: Optional[ActivityTypes]
|
ACTIVITY_TYPE: Optional[ActivityTypes] = None
|
||||||
NO_CONTEXT = False
|
ALLOWED_OBJECT_TYPES: List[ActivityTypes] = []
|
||||||
ALLOWED_OBJECT_TYPES = None # type: List[ActivityTypes]
|
|
||||||
|
|
||||||
def __init__(self, **kwargs) -> None:
|
def __init__(self, **kwargs) -> None:
|
||||||
if not self.ACTIVITY_TYPE:
|
if not self.ACTIVITY_TYPE:
|
||||||
|
@ -92,7 +91,7 @@ class BaseActivity(object):
|
||||||
if kwargs.get('type') is not None and kwargs.pop('type') != self.ACTIVITY_TYPE.value:
|
if kwargs.get('type') is not None and kwargs.pop('type') != self.ACTIVITY_TYPE.value:
|
||||||
raise ValueError('Expect the type to be {}'.format(self.ACTIVITY_TYPE))
|
raise ValueError('Expect the type to be {}'.format(self.ACTIVITY_TYPE))
|
||||||
|
|
||||||
self._data = {'type': self.ACTIVITY_TYPE.value} # type: Dict[str, Any]
|
self._data: Dict[str, Any] = {'type': self.ACTIVITY_TYPE.value}
|
||||||
|
|
||||||
if 'id' in kwargs:
|
if 'id' in kwargs:
|
||||||
self._data['id'] = kwargs.pop('id')
|
self._data['id'] = kwargs.pop('id')
|
||||||
|
@ -230,7 +229,7 @@ class BaseActivity(object):
|
||||||
|
|
||||||
p = parse_activity(obj)
|
p = parse_activity(obj)
|
||||||
|
|
||||||
self.__obj = p # type: BaseActivity
|
self.__obj: BaseActivity = p
|
||||||
return p
|
return p
|
||||||
|
|
||||||
def _to_dict(self, data: ObjectType) -> ObjectType:
|
def _to_dict(self, data: ObjectType) -> ObjectType:
|
||||||
|
@ -267,6 +266,9 @@ class BaseActivity(object):
|
||||||
def _undo_inbox(self) -> None:
|
def _undo_inbox(self) -> None:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def _should_purge_cache(self) -> bool:
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
def process_from_inbox(self) -> None:
|
def process_from_inbox(self) -> None:
|
||||||
self.verify()
|
self.verify()
|
||||||
actor = self.get_actor()
|
actor = self.get_actor()
|
||||||
|
@ -332,7 +334,7 @@ class BaseActivity(object):
|
||||||
def recipients(self) -> List[str]:
|
def recipients(self) -> List[str]:
|
||||||
recipients = self._recipients()
|
recipients = self._recipients()
|
||||||
|
|
||||||
out = [] # type: List[str]
|
out: List[str] = []
|
||||||
for recipient in recipients:
|
for recipient in recipients:
|
||||||
if recipient in PUBLIC_INSTANCES:
|
if recipient in PUBLIC_INSTANCES:
|
||||||
if recipient not in out:
|
if recipient not in out:
|
||||||
|
@ -455,6 +457,10 @@ class Follow(BaseActivity):
|
||||||
def build_undo(self) -> BaseActivity:
|
def build_undo(self) -> BaseActivity:
|
||||||
return Undo(object=self.to_dict(embed=True))
|
return Undo(object=self.to_dict(embed=True))
|
||||||
|
|
||||||
|
def _should_purge_cache(self) -> bool:
|
||||||
|
# Receiving a follow activity in the inbox should reset the application cache
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
class Accept(BaseActivity):
|
class Accept(BaseActivity):
|
||||||
ACTIVITY_TYPE = ActivityTypes.ACCEPT
|
ACTIVITY_TYPE = ActivityTypes.ACCEPT
|
||||||
|
@ -468,6 +474,12 @@ class Accept(BaseActivity):
|
||||||
if DB.following.find({'remote_actor': remote_actor}).count() == 0:
|
if DB.following.find({'remote_actor': remote_actor}).count() == 0:
|
||||||
DB.following.insert_one({'remote_actor': remote_actor})
|
DB.following.insert_one({'remote_actor': remote_actor})
|
||||||
|
|
||||||
|
def _should_purge_cache(self) -> bool:
|
||||||
|
# Receiving an accept activity in the inbox should reset the application cache
|
||||||
|
# (a follow request has been accepted)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Undo(BaseActivity):
|
class Undo(BaseActivity):
|
||||||
ACTIVITY_TYPE = ActivityTypes.UNDO
|
ACTIVITY_TYPE = ActivityTypes.UNDO
|
||||||
|
@ -628,7 +640,7 @@ class Update(BaseActivity):
|
||||||
obj = self.get_object()
|
obj = self.get_object()
|
||||||
|
|
||||||
update_prefix = 'activity.object.'
|
update_prefix = 'activity.object.'
|
||||||
update = {'$set': dict(), '$unset': dict()} # type: Dict[str, Any]
|
update: Dict[str, Any] = {'$set': dict(), '$unset': dict()}
|
||||||
update['$set'][f'{update_prefix}updated'] = datetime.utcnow().replace(microsecond=0).isoformat() + 'Z'
|
update['$set'][f'{update_prefix}updated'] = datetime.utcnow().replace(microsecond=0).isoformat() + 'Z'
|
||||||
for k, v in obj._data.items():
|
for k, v in obj._data.items():
|
||||||
if k in ['id', 'type']:
|
if k in ['id', 'type']:
|
||||||
|
@ -734,7 +746,7 @@ class Note(BaseActivity):
|
||||||
|
|
||||||
def _recipients(self) -> List[str]:
|
def _recipients(self) -> List[str]:
|
||||||
# TODO(tsileo): audience support?
|
# TODO(tsileo): audience support?
|
||||||
recipients = [] # type: List[str]
|
recipients: List[str] = []
|
||||||
|
|
||||||
# If the note is public, we publish it to the defined "public instances"
|
# If the note is public, we publish it to the defined "public instances"
|
||||||
if AS_PUBLIC in self._data.get('to', []):
|
if AS_PUBLIC in self._data.get('to', []):
|
||||||
|
@ -847,7 +859,7 @@ def build_inbox_json_feed(path: str, request_cursor: Optional[str] = None) -> Di
|
||||||
data = []
|
data = []
|
||||||
cursor = None
|
cursor = None
|
||||||
|
|
||||||
q = {'type': 'Create'} # type: Dict[str, Any]
|
q: Dict[str, Any] = {'type': 'Create'}
|
||||||
if request_cursor:
|
if request_cursor:
|
||||||
q['_id'] = {'$lt': request_cursor}
|
q['_id'] = {'$lt': request_cursor}
|
||||||
|
|
||||||
|
@ -889,7 +901,7 @@ def parse_collection(payload: Optional[Dict[str, Any]] = None, url: Optional[str
|
||||||
return [doc['remote_actor'] for doc in DB.following.find()]
|
return [doc['remote_actor'] for doc in DB.following.find()]
|
||||||
|
|
||||||
# Go through all the pages
|
# Go through all the pages
|
||||||
out = [] # type: List[str]
|
out: List[str] = []
|
||||||
if url:
|
if url:
|
||||||
resp = requests.get(url, headers={'Accept': 'application/activity+json'})
|
resp = requests.get(url, headers={'Accept': 'application/activity+json'})
|
||||||
resp.raise_for_status()
|
resp.raise_for_status()
|
||||||
|
|
11
app.py
11
app.py
|
@ -46,6 +46,8 @@ from config import ACTOR_SERVICE
|
||||||
from config import OBJECT_SERVICE
|
from config import OBJECT_SERVICE
|
||||||
from config import PASS
|
from config import PASS
|
||||||
from config import HEADERS
|
from config import HEADERS
|
||||||
|
from config import VERSION
|
||||||
|
from config import custom_cache_purge_hook
|
||||||
from utils.httpsig import HTTPSigAuth, verify_request
|
from utils.httpsig import HTTPSigAuth, verify_request
|
||||||
from utils.key import get_secret_key
|
from utils.key import get_secret_key
|
||||||
from utils.webfinger import get_remote_follow_template
|
from utils.webfinger import get_remote_follow_template
|
||||||
|
@ -69,7 +71,11 @@ def verify_pass(pwd):
|
||||||
|
|
||||||
@app.context_processor
|
@app.context_processor
|
||||||
def inject_config():
|
def inject_config():
|
||||||
return dict(config=config, logged_in=session.get('logged_in', False))
|
return dict(
|
||||||
|
microblogpub_version=VERSION,
|
||||||
|
config=config,
|
||||||
|
logged_in=session.get('logged_in', False),
|
||||||
|
)
|
||||||
|
|
||||||
@app.after_request
|
@app.after_request
|
||||||
def set_x_powered_by(response):
|
def set_x_powered_by(response):
|
||||||
|
@ -453,6 +459,9 @@ def outbox():
|
||||||
|
|
||||||
activity.post_to_outbox()
|
activity.post_to_outbox()
|
||||||
|
|
||||||
|
# Purge the cache if a custom hook is set, as new content was published
|
||||||
|
custom_cache_purge_hook()
|
||||||
|
|
||||||
return Response(status=201, headers={'Location': activity.id})
|
return Response(status=201, headers={'Location': activity.id})
|
||||||
|
|
||||||
|
|
||||||
|
|
12
config.py
12
config.py
|
@ -1,3 +1,4 @@
|
||||||
|
import subprocess
|
||||||
import os
|
import os
|
||||||
import yaml
|
import yaml
|
||||||
from pymongo import MongoClient
|
from pymongo import MongoClient
|
||||||
|
@ -7,8 +8,17 @@ from utils.key import Key
|
||||||
from utils.actor_service import ActorService
|
from utils.actor_service import ActorService
|
||||||
from utils.object_service import ObjectService
|
from utils.object_service import ObjectService
|
||||||
|
|
||||||
|
def noop():
|
||||||
|
pass
|
||||||
|
|
||||||
VERSION = '1.0.0'
|
|
||||||
|
CUSTOM_CACHE_HOOKS = False
|
||||||
|
try:
|
||||||
|
from cache_hooks import purge as custom_cache_purge_hook
|
||||||
|
except ModuleNotFoundError:
|
||||||
|
custom_cache_purge_hook = noop
|
||||||
|
|
||||||
|
VERSION = subprocess.check_output(['git', 'describe', '--always']).split()[0].decode('utf-8')
|
||||||
|
|
||||||
CTX_AS = 'https://www.w3.org/ns/activitystreams'
|
CTX_AS = 'https://www.w3.org/ns/activitystreams'
|
||||||
CTX_SECURITY = 'https://w3id.org/security/v1'
|
CTX_SECURITY = 'https://w3id.org/security/v1'
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
<div>
|
<div>
|
||||||
Powered by <a href="https://microblog.pub">microblog.pub</a> (<a href="https://github.com/tsileo/microblog.pub">source code</a>) and the <a href="https://activitypub.rocks/">ActivityPub</a> protocol
|
Powered by <a href="https://microblog.pub">microblog.pub</a> <small class="microblogpub-version"><code>{{ microblogpub_version }}</code></small> (<a href="https://github.com/tsileo/microblog.pub">source code</a>) and the <a href="https://activitypub.rocks/">ActivityPub</a> protocol
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|
Loading…
Reference in a new issue