mirror of
https://codeberg.org/rimu/pyfedi
synced 2025-01-23 11:26:56 -08:00
move long-running tasks to separate background process (celery + redis)
This commit is contained in:
parent
684e68c3fd
commit
6182240ad3
17 changed files with 595 additions and 525 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -161,3 +161,4 @@ cython_debug/
|
||||||
.idea/
|
.idea/
|
||||||
app/static/*.css.map
|
app/static/*.css.map
|
||||||
/app/static/media/
|
/app/static/media/
|
||||||
|
celery_worker.py
|
||||||
|
|
|
@ -13,6 +13,7 @@ from flask_mail import Mail
|
||||||
from flask_moment import Moment
|
from flask_moment import Moment
|
||||||
from flask_babel import Babel, lazy_gettext as _l
|
from flask_babel import Babel, lazy_gettext as _l
|
||||||
from flask_caching import Cache
|
from flask_caching import Cache
|
||||||
|
from celery import Celery
|
||||||
from sqlalchemy_searchable import make_searchable
|
from sqlalchemy_searchable import make_searchable
|
||||||
|
|
||||||
from config import Config
|
from config import Config
|
||||||
|
@ -28,6 +29,7 @@ bootstrap = Bootstrap5()
|
||||||
moment = Moment()
|
moment = Moment()
|
||||||
babel = Babel()
|
babel = Babel()
|
||||||
cache = Cache()
|
cache = Cache()
|
||||||
|
celery = Celery(__name__, broker=Config.CELERY_BROKER_URL)
|
||||||
|
|
||||||
|
|
||||||
def create_app(config_class=Config):
|
def create_app(config_class=Config):
|
||||||
|
@ -43,6 +45,7 @@ def create_app(config_class=Config):
|
||||||
make_searchable(db.metadata)
|
make_searchable(db.metadata)
|
||||||
babel.init_app(app, locale_selector=get_locale)
|
babel.init_app(app, locale_selector=get_locale)
|
||||||
cache.init_app(app)
|
cache.init_app(app)
|
||||||
|
celery.conf.update(app.config)
|
||||||
|
|
||||||
from app.main import bp as main_bp
|
from app.main import bp as main_bp
|
||||||
app.register_blueprint(main_bp)
|
app.register_blueprint(main_bp)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from app import db, constants, cache
|
from app import db, constants, cache, celery
|
||||||
from app.activitypub import bp
|
from app.activitypub import bp
|
||||||
from flask import request, Response, current_app, abort, jsonify, json, g
|
from flask import request, Response, current_app, abort, jsonify, json, g
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ from app.post.routes import continue_discussion, show_post
|
||||||
from app.user.routes import show_profile
|
from app.user.routes import show_profile
|
||||||
from app.constants import POST_TYPE_LINK, POST_TYPE_IMAGE, SUBSCRIPTION_MEMBER
|
from app.constants import POST_TYPE_LINK, POST_TYPE_IMAGE, SUBSCRIPTION_MEMBER
|
||||||
from app.models import User, Community, CommunityJoinRequest, CommunityMember, CommunityBan, ActivityPubLog, Post, \
|
from app.models import User, Community, CommunityJoinRequest, CommunityMember, CommunityBan, ActivityPubLog, Post, \
|
||||||
PostReply, Instance, PostVote, PostReplyVote, File, AllowedInstances, BannedInstances, utcnow
|
PostReply, Instance, PostVote, PostReplyVote, File, AllowedInstances, BannedInstances, utcnow, Site
|
||||||
from app.activitypub.util import public_key, users_total, active_half_year, active_month, local_posts, local_comments, \
|
from app.activitypub.util import public_key, users_total, active_half_year, active_month, local_posts, local_comments, \
|
||||||
post_to_activity, find_actor_or_create, default_context, instance_blocked, find_reply_parent, find_liked_object, \
|
post_to_activity, find_actor_or_create, default_context, instance_blocked, find_reply_parent, find_liked_object, \
|
||||||
lemmy_site_data, instance_weight, is_activitypub_request, downvote_post_reply, downvote_post, upvote_post_reply, \
|
lemmy_site_data, instance_weight, is_activitypub_request, downvote_post_reply, downvote_post, upvote_post_reply, \
|
||||||
|
@ -98,7 +98,7 @@ def nodeinfo2():
|
||||||
"localPosts": local_posts(),
|
"localPosts": local_posts(),
|
||||||
"localComments": local_comments()
|
"localComments": local_comments()
|
||||||
},
|
},
|
||||||
"openRegistrations": True
|
"openRegistrations": g.site.registration_mode == 'Open'
|
||||||
}
|
}
|
||||||
return jsonify(nodeinfo_data)
|
return jsonify(nodeinfo_data)
|
||||||
|
|
||||||
|
@ -278,10 +278,8 @@ def shared_inbox():
|
||||||
db.session.add(activity_log)
|
db.session.add(activity_log)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
return ''
|
return ''
|
||||||
else:
|
|
||||||
if 'id' in request_json:
|
|
||||||
activity_log.activity_id = request_json['id']
|
|
||||||
|
|
||||||
|
if 'id' in request_json:
|
||||||
if activity_already_ingested(request_json['id']): # Lemmy has an extremely short POST timeout and tends to retry unnecessarily. Ignore their retries.
|
if activity_already_ingested(request_json['id']): # Lemmy has an extremely short POST timeout and tends to retry unnecessarily. Ignore their retries.
|
||||||
activity_log.result = 'ignored'
|
activity_log.result = 'ignored'
|
||||||
db.session.add(activity_log)
|
db.session.add(activity_log)
|
||||||
|
@ -290,6 +288,7 @@ def shared_inbox():
|
||||||
|
|
||||||
activity_log.activity_id = request_json['id']
|
activity_log.activity_id = request_json['id']
|
||||||
activity_log.activity_json = json.dumps(request_json)
|
activity_log.activity_json = json.dumps(request_json)
|
||||||
|
activity_log.result = 'processing'
|
||||||
|
|
||||||
# Mastodon spams the whole fediverse whenever any of their users are deleted. Ignore them, for now. The Activity includes the Actor signature so it should be possible to verify the POST and do the delete if valid, without a call to find_actor_or_create() and all the network activity that involves. One day.
|
# Mastodon spams the whole fediverse whenever any of their users are deleted. Ignore them, for now. The Activity includes the Actor signature so it should be possible to verify the POST and do the delete if valid, without a call to find_actor_or_create() and all the network activity that involves. One day.
|
||||||
if 'type' in request_json and request_json['type'] == 'Delete' and request_json['id'].endswith('#delete'):
|
if 'type' in request_json and request_json['type'] == 'Delete' and request_json['id'].endswith('#delete'):
|
||||||
|
@ -298,10 +297,40 @@ def shared_inbox():
|
||||||
db.session.add(activity_log)
|
db.session.add(activity_log)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
return ''
|
return ''
|
||||||
|
else:
|
||||||
|
db.session.add(activity_log)
|
||||||
|
db.session.commit()
|
||||||
|
else:
|
||||||
|
activity_log.activity_id = ''
|
||||||
|
activity_log.activity_json = json.dumps(request_json)
|
||||||
|
db.session.add(activity_log)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
actor = find_actor_or_create(request_json['actor']) if 'actor' in request_json else None
|
actor = find_actor_or_create(request_json['actor']) if 'actor' in request_json else None
|
||||||
if actor is not None:
|
if actor is not None:
|
||||||
if HttpSignature.verify_request(request, actor.public_key, skip_date=True):
|
if HttpSignature.verify_request(request, actor.public_key, skip_date=True):
|
||||||
|
if current_app.debug:
|
||||||
|
process_inbox_request(request_json, activity_log.id)
|
||||||
|
else:
|
||||||
|
process_inbox_request.delay(request_json, activity_log.id)
|
||||||
|
return ''
|
||||||
|
else:
|
||||||
|
activity_log.exception_message = 'Could not verify signature'
|
||||||
|
else:
|
||||||
|
actor_name = request_json['actor'] if 'actor' in request_json else ''
|
||||||
|
activity_log.exception_message = f'Actor could not be found: {actor_name}'
|
||||||
|
|
||||||
|
if activity_log.exception_message is not None:
|
||||||
|
activity_log.result = 'failure'
|
||||||
|
db.session.commit()
|
||||||
|
return ''
|
||||||
|
|
||||||
|
|
||||||
|
@celery.task
|
||||||
|
def process_inbox_request(request_json, activitypublog_id):
|
||||||
|
with current_app.app_context():
|
||||||
|
activity_log = ActivityPubLog.query.get(activitypublog_id)
|
||||||
|
site = Site.query.get(1) # can't use g.site because celery doesn't use Flask's g variable
|
||||||
if 'type' in request_json:
|
if 'type' in request_json:
|
||||||
activity_log.activity_type = request_json['type']
|
activity_log.activity_type = request_json['type']
|
||||||
if not instance_blocked(request_json['id']):
|
if not instance_blocked(request_json['id']):
|
||||||
|
@ -313,32 +342,27 @@ def shared_inbox():
|
||||||
if community_ap_id == 'https://www.w3.org/ns/activitystreams#Public': # kbin does this when posting a reply
|
if community_ap_id == 'https://www.w3.org/ns/activitystreams#Public': # kbin does this when posting a reply
|
||||||
if 'to' in request_json['object'] and request_json['object']['to']:
|
if 'to' in request_json['object'] and request_json['object']['to']:
|
||||||
community_ap_id = request_json['object']['to'][0]
|
community_ap_id = request_json['object']['to'][0]
|
||||||
if community_ap_id == 'https://www.w3.org/ns/activitystreams#Public' and 'cc' in request_json['object'] and request_json['object']['cc']:
|
if community_ap_id == 'https://www.w3.org/ns/activitystreams#Public' and 'cc' in \
|
||||||
|
request_json['object'] and request_json['object']['cc']:
|
||||||
community_ap_id = request_json['object']['cc'][0]
|
community_ap_id = request_json['object']['cc'][0]
|
||||||
elif 'cc' in request_json['object'] and request_json['object']['cc']:
|
elif 'cc' in request_json['object'] and request_json['object']['cc']:
|
||||||
community_ap_id = request_json['object']['cc'][0]
|
community_ap_id = request_json['object']['cc'][0]
|
||||||
community = find_actor_or_create(community_ap_id)
|
community = find_actor_or_create(community_ap_id)
|
||||||
user = find_actor_or_create(user_ap_id)
|
user = find_actor_or_create(user_ap_id)
|
||||||
if (user and not user.is_local()) and community:
|
if (user and not user.is_local()) and community:
|
||||||
user.last_seen = community.last_active = g.site.last_active = utcnow()
|
user.last_seen = community.last_active = site.last_active = utcnow()
|
||||||
|
|
||||||
object_type = request_json['object']['type']
|
object_type = request_json['object']['type']
|
||||||
new_content_types = ['Page', 'Article', 'Link', 'Note']
|
new_content_types = ['Page', 'Article', 'Link', 'Note']
|
||||||
if object_type in new_content_types: # create a new post
|
if object_type in new_content_types: # create a new post
|
||||||
in_reply_to = request_json['object']['inReplyTo'] if 'inReplyTo' in \
|
in_reply_to = request_json['object']['inReplyTo'] if 'inReplyTo' in request_json['object'] else None
|
||||||
request_json[
|
|
||||||
'object'] else None
|
|
||||||
if not in_reply_to:
|
if not in_reply_to:
|
||||||
post = Post(user_id=user.id, community_id=community.id,
|
post = Post(user_id=user.id, community_id=community.id,
|
||||||
title=request_json['object']['name'],
|
title=request_json['object']['name'],
|
||||||
comments_enabled=request_json['object'][
|
comments_enabled=request_json['object']['commentsEnabled'],
|
||||||
'commentsEnabled'],
|
sticky=request_json['object']['stickied'] if 'stickied' in request_json['object'] else False,
|
||||||
sticky=request_json['object']['stickied'] if 'stickied' in
|
|
||||||
request_json[
|
|
||||||
'object'] else False,
|
|
||||||
nsfw=request_json['object']['sensitive'],
|
nsfw=request_json['object']['sensitive'],
|
||||||
nsfl=request_json['object']['nsfl'] if 'nsfl' in request_json[
|
nsfl=request_json['object']['nsfl'] if 'nsfl' in request_json['object'] else False,
|
||||||
'object'] else False,
|
|
||||||
ap_id=request_json['object']['id'],
|
ap_id=request_json['object']['id'],
|
||||||
ap_create_id=request_json['id'],
|
ap_create_id=request_json['id'],
|
||||||
ap_announce_id=None,
|
ap_announce_id=None,
|
||||||
|
@ -346,16 +370,13 @@ def shared_inbox():
|
||||||
up_votes=1,
|
up_votes=1,
|
||||||
score=instance_weight(user.ap_domain)
|
score=instance_weight(user.ap_domain)
|
||||||
)
|
)
|
||||||
if 'source' in request_json['object'] and \
|
if 'source' in request_json['object'] and request_json['object']['source']['mediaType'] == 'text/markdown':
|
||||||
request_json['object']['source'][
|
|
||||||
'mediaType'] == 'text/markdown':
|
|
||||||
post.body = request_json['object']['source']['content']
|
post.body = request_json['object']['source']['content']
|
||||||
post.body_html = markdown_to_html(post.body)
|
post.body_html = markdown_to_html(post.body)
|
||||||
elif 'content' in request_json['object'] and request_json['object']['content'] is not None:
|
elif 'content' in request_json['object'] and request_json['object']['content'] is not None:
|
||||||
post.body_html = allowlist_html(request_json['object']['content'])
|
post.body_html = allowlist_html(request_json['object']['content'])
|
||||||
post.body = html_to_markdown(post.body_html)
|
post.body = html_to_markdown(post.body_html)
|
||||||
if 'attachment' in request_json['object'] and \
|
if 'attachment' in request_json['object'] and len(request_json['object']['attachment']) > 0 and \
|
||||||
len(request_json['object']['attachment']) > 0 and \
|
|
||||||
'type' in request_json['object']['attachment'][0]:
|
'type' in request_json['object']['attachment'][0]:
|
||||||
if request_json['object']['attachment'][0]['type'] == 'Link':
|
if request_json['object']['attachment'][0]['type'] == 'Link':
|
||||||
post.url = request_json['object']['attachment'][0]['href']
|
post.url = request_json['object']['attachment'][0]['href']
|
||||||
|
@ -369,7 +390,6 @@ def shared_inbox():
|
||||||
else:
|
else:
|
||||||
post = None
|
post = None
|
||||||
activity_log.exception_message = domain.name + ' is blocked by admin'
|
activity_log.exception_message = domain.name + ' is blocked by admin'
|
||||||
activity_log.result = 'failure'
|
|
||||||
if 'image' in request_json['object']:
|
if 'image' in request_json['object']:
|
||||||
image = File(source_url=request_json['object']['image']['url'])
|
image = File(source_url=request_json['object']['image']['url'])
|
||||||
db.session.add(image)
|
db.session.add(image)
|
||||||
|
@ -398,13 +418,11 @@ def shared_inbox():
|
||||||
ap_create_id=request_json['id'],
|
ap_create_id=request_json['id'],
|
||||||
ap_announce_id=None)
|
ap_announce_id=None)
|
||||||
if 'source' in request_json['object'] and \
|
if 'source' in request_json['object'] and \
|
||||||
request_json['object']['source'][
|
request_json['object']['source']['mediaType'] == 'text/markdown':
|
||||||
'mediaType'] == 'text/markdown':
|
|
||||||
post_reply.body = request_json['object']['source']['content']
|
post_reply.body = request_json['object']['source']['content']
|
||||||
post_reply.body_html = markdown_to_html(post_reply.body)
|
post_reply.body_html = markdown_to_html(post_reply.body)
|
||||||
elif 'content' in request_json['object']:
|
elif 'content' in request_json['object']:
|
||||||
post_reply.body_html = allowlist_html(
|
post_reply.body_html = allowlist_html(request_json['object']['content'])
|
||||||
request_json['object']['content'])
|
|
||||||
post_reply.body = html_to_markdown(post_reply.body_html)
|
post_reply.body = html_to_markdown(post_reply.body_html)
|
||||||
|
|
||||||
if post_reply is not None:
|
if post_reply is not None:
|
||||||
|
@ -434,7 +452,7 @@ def shared_inbox():
|
||||||
community = find_actor_or_create(community_ap_id)
|
community = find_actor_or_create(community_ap_id)
|
||||||
user = find_actor_or_create(user_ap_id)
|
user = find_actor_or_create(user_ap_id)
|
||||||
if (user and not user.is_local()) and community:
|
if (user and not user.is_local()) and community:
|
||||||
user.last_seen = community.last_active = g.site.last_active = utcnow()
|
user.last_seen = community.last_active = site.last_active = utcnow()
|
||||||
object_type = request_json['object']['object']['type']
|
object_type = request_json['object']['object']['type']
|
||||||
new_content_types = ['Page', 'Article', 'Link', 'Note']
|
new_content_types = ['Page', 'Article', 'Link', 'Note']
|
||||||
if object_type in new_content_types: # create a new post
|
if object_type in new_content_types: # create a new post
|
||||||
|
@ -446,7 +464,7 @@ def shared_inbox():
|
||||||
title=request_json['object']['object']['name'],
|
title=request_json['object']['object']['name'],
|
||||||
comments_enabled=request_json['object']['object']['commentsEnabled'],
|
comments_enabled=request_json['object']['object']['commentsEnabled'],
|
||||||
sticky=request_json['object']['object']['stickied'] if 'stickied' in request_json['object']['object'] else False,
|
sticky=request_json['object']['object']['stickied'] if 'stickied' in request_json['object']['object'] else False,
|
||||||
nsfw=request_json['object']['object']['sensitive'],
|
nsfw=request_json['object']['object']['sensitive'] if 'sensitive' in request_json['object']['object'] else False,
|
||||||
nsfl=request_json['object']['object']['nsfl'] if 'nsfl' in request_json['object']['object'] else False,
|
nsfl=request_json['object']['object']['nsfl'] if 'nsfl' in request_json['object']['object'] else False,
|
||||||
ap_id=request_json['object']['object']['id'],
|
ap_id=request_json['object']['object']['id'],
|
||||||
ap_create_id=request_json['object']['id'],
|
ap_create_id=request_json['object']['id'],
|
||||||
|
@ -475,7 +493,6 @@ def shared_inbox():
|
||||||
else:
|
else:
|
||||||
post = None
|
post = None
|
||||||
activity_log.exception_message = domain.name + ' is blocked by admin'
|
activity_log.exception_message = domain.name + ' is blocked by admin'
|
||||||
activity_log.result = 'failure'
|
|
||||||
if 'image' in request_json['object']['object']:
|
if 'image' in request_json['object']['object']:
|
||||||
image = File(source_url=request_json['object']['object']['image']['url'])
|
image = File(source_url=request_json['object']['object']['image']['url'])
|
||||||
db.session.add(image)
|
db.session.add(image)
|
||||||
|
@ -497,8 +514,7 @@ def shared_inbox():
|
||||||
ap_create_id=request_json['object']['id'],
|
ap_create_id=request_json['object']['id'],
|
||||||
ap_announce_id=request_json['id'])
|
ap_announce_id=request_json['id'])
|
||||||
if 'source' in request_json['object']['object'] and \
|
if 'source' in request_json['object']['object'] and \
|
||||||
request_json['object']['object']['source'][
|
request_json['object']['object']['source']['mediaType'] == 'text/markdown':
|
||||||
'mediaType'] == 'text/markdown':
|
|
||||||
post_reply.body = request_json['object']['object']['source']['content']
|
post_reply.body = request_json['object']['object']['source']['content']
|
||||||
post_reply.body_html = markdown_to_html(post_reply.body)
|
post_reply.body_html = markdown_to_html(post_reply.body)
|
||||||
elif 'content' in request_json['object']['object']:
|
elif 'content' in request_json['object']['object']:
|
||||||
|
@ -546,7 +562,7 @@ def shared_inbox():
|
||||||
# todo: if vote was on content in local community, federate the vote out to followers
|
# todo: if vote was on content in local community, federate the vote out to followers
|
||||||
elif request_json['object']['type'] == 'Dislike':
|
elif request_json['object']['type'] == 'Dislike':
|
||||||
activity_log.activity_type = request_json['object']['type']
|
activity_log.activity_type = request_json['object']['type']
|
||||||
if g.site.enable_downvotes is False:
|
if site.enable_downvotes is False:
|
||||||
activity_log.exception_message = 'Dislike ignored because of allow_dislike setting'
|
activity_log.exception_message = 'Dislike ignored because of allow_dislike setting'
|
||||||
else:
|
else:
|
||||||
user_ap_id = request_json['object']['actor']
|
user_ap_id = request_json['object']['actor']
|
||||||
|
@ -578,8 +594,7 @@ def shared_inbox():
|
||||||
community = find_actor_or_create(community_ap_id)
|
community = find_actor_or_create(community_ap_id)
|
||||||
if user is not None and community is not None:
|
if user is not None and community is not None:
|
||||||
# check if user is banned from this community
|
# check if user is banned from this community
|
||||||
banned = CommunityBan.query.filter_by(user_id=user.id,
|
banned = CommunityBan.query.filter_by(user_id=user.id, community_id=community.id).first()
|
||||||
community_id=community.id).first()
|
|
||||||
if banned is None:
|
if banned is None:
|
||||||
user.last_seen = utcnow()
|
user.last_seen = utcnow()
|
||||||
if community_membership(user, community) != SUBSCRIPTION_MEMBER:
|
if community_membership(user, community) != SUBSCRIPTION_MEMBER:
|
||||||
|
@ -625,8 +640,7 @@ def shared_inbox():
|
||||||
user = find_actor_or_create(user_ap_id)
|
user = find_actor_or_create(user_ap_id)
|
||||||
community = find_actor_or_create(community_ap_id)
|
community = find_actor_or_create(community_ap_id)
|
||||||
if user and community:
|
if user and community:
|
||||||
join_request = CommunityJoinRequest.query.filter_by(user_id=user.id,
|
join_request = CommunityJoinRequest.query.filter_by(user_id=user.id, community_id=community.id).first()
|
||||||
community_id=community.id).first()
|
|
||||||
if join_request:
|
if join_request:
|
||||||
member = CommunityMember(user_id=user.id, community_id=community.id)
|
member = CommunityMember(user_id=user.id, community_id=community.id)
|
||||||
db.session.add(member)
|
db.session.add(member)
|
||||||
|
@ -644,8 +658,7 @@ def shared_inbox():
|
||||||
if user and community:
|
if user and community:
|
||||||
user.last_seen = utcnow()
|
user.last_seen = utcnow()
|
||||||
member = CommunityMember.query.filter_by(user_id=user.id, community_id=community.id).first()
|
member = CommunityMember.query.filter_by(user_id=user.id, community_id=community.id).first()
|
||||||
join_request = CommunityJoinRequest.query.filter_by(user_id=user.id,
|
join_request = CommunityJoinRequest.query.filter_by(user_id=user.id, community_id=community.id).first()
|
||||||
community_id=community.id).first()
|
|
||||||
if member:
|
if member:
|
||||||
db.session.delete(member)
|
db.session.delete(member)
|
||||||
if join_request:
|
if join_request:
|
||||||
|
@ -720,8 +733,7 @@ def shared_inbox():
|
||||||
if request_json['object']['type'] == 'Page': # Editing a post
|
if request_json['object']['type'] == 'Page': # Editing a post
|
||||||
post = Post.query.filter_by(ap_id=request_json['object']['id']).first()
|
post = Post.query.filter_by(ap_id=request_json['object']['id']).first()
|
||||||
if post:
|
if post:
|
||||||
if 'source' in request_json['object'] and \
|
if 'source' in request_json['object'] and request_json['object']['source']['mediaType'] == 'text/markdown':
|
||||||
request_json['object']['source']['mediaType'] == 'text/markdown':
|
|
||||||
post.body = request_json['object']['source']['content']
|
post.body = request_json['object']['source']['content']
|
||||||
post.body_html = markdown_to_html(post.body)
|
post.body_html = markdown_to_html(post.body)
|
||||||
elif 'content' in request_json['object']:
|
elif 'content' in request_json['object']:
|
||||||
|
@ -733,8 +745,7 @@ def shared_inbox():
|
||||||
elif request_json['object']['type'] == 'Note': # Editing a reply
|
elif request_json['object']['type'] == 'Note': # Editing a reply
|
||||||
reply = PostReply.query.filter_by(ap_id=request_json['object']['id']).first()
|
reply = PostReply.query.filter_by(ap_id=request_json['object']['id']).first()
|
||||||
if reply:
|
if reply:
|
||||||
if 'source' in request_json['object'] and \
|
if 'source' in request_json['object'] and request_json['object']['source']['mediaType'] == 'text/markdown':
|
||||||
request_json['object']['source']['mediaType'] == 'text/markdown':
|
|
||||||
reply.body = request_json['object']['source']['content']
|
reply.body = request_json['object']['source']['content']
|
||||||
reply.body_html = markdown_to_html(reply.body)
|
reply.body_html = markdown_to_html(reply.body)
|
||||||
elif 'content' in request_json['object']:
|
elif 'content' in request_json['object']:
|
||||||
|
@ -808,16 +819,10 @@ def shared_inbox():
|
||||||
post.flush_cache()
|
post.flush_cache()
|
||||||
else:
|
else:
|
||||||
activity_log.exception_message = 'Instance banned'
|
activity_log.exception_message = 'Instance banned'
|
||||||
else:
|
|
||||||
activity_log.exception_message = 'Could not verify signature'
|
|
||||||
else:
|
|
||||||
activity_log.exception_message = 'Actor could not be found: ' + request_json['actor']
|
|
||||||
|
|
||||||
if activity_log.exception_message is not None:
|
if activity_log.exception_message is not None:
|
||||||
activity_log.result = 'failure'
|
activity_log.result = 'failure'
|
||||||
db.session.add(activity_log)
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
return ''
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/c/<actor>/outbox', methods=['GET'])
|
@bp.route('/c/<actor>/outbox', methods=['GET'])
|
||||||
|
|
|
@ -215,7 +215,6 @@ def find_actor_or_create(actor: str) -> Union[User, Community, None]:
|
||||||
if 'rel' in links and links['rel'] == 'self': # this contains the URL of the activitypub profile
|
if 'rel' in links and links['rel'] == 'self': # this contains the URL of the activitypub profile
|
||||||
type = links['type'] if 'type' in links else 'application/activity+json'
|
type = links['type'] if 'type' in links else 'application/activity+json'
|
||||||
# retrieve the activitypub profile
|
# retrieve the activitypub profile
|
||||||
print('****', links['href'])
|
|
||||||
actor_data = get_request(links['href'], headers={'Accept': type})
|
actor_data = get_request(links['href'], headers={'Accept': type})
|
||||||
# to see the structure of the json contained in actor_data, do a GET to https://lemmy.world/c/technology with header Accept: application/activity+json
|
# to see the structure of the json contained in actor_data, do a GET to https://lemmy.world/c/technology with header Accept: application/activity+json
|
||||||
if actor_data.status_code == 200:
|
if actor_data.status_code == 200:
|
||||||
|
@ -462,7 +461,10 @@ def find_instance_id(server):
|
||||||
try:
|
try:
|
||||||
instance_data = get_request(f"https://{server}", headers={'Accept': 'application/activity+json'})
|
instance_data = get_request(f"https://{server}", headers={'Accept': 'application/activity+json'})
|
||||||
except:
|
except:
|
||||||
return None
|
new_instance = Instance(domain=server, software='unknown', created_at=utcnow())
|
||||||
|
db.session.add(new_instance)
|
||||||
|
db.session.commit()
|
||||||
|
return new_instance.id
|
||||||
if instance_data.status_code == 200:
|
if instance_data.status_code == 200:
|
||||||
try:
|
try:
|
||||||
instance_json = instance_data.json()
|
instance_json = instance_data.json()
|
||||||
|
@ -487,7 +489,11 @@ def find_instance_id(server):
|
||||||
db.session.add(new_instance)
|
db.session.add(new_instance)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
return new_instance.id
|
return new_instance.id
|
||||||
return None
|
else:
|
||||||
|
new_instance = Instance(domain=server, software='unknown', created_at=utcnow())
|
||||||
|
db.session.add(new_instance)
|
||||||
|
db.session.commit()
|
||||||
|
return new_instance.id
|
||||||
|
|
||||||
|
|
||||||
# alter the effect of upvotes based on their instance. Default to 1.0
|
# alter the effect of upvotes based on their instance. Default to 1.0
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from flask import current_app, render_template, escape
|
from flask import current_app, render_template, escape
|
||||||
from app import db
|
from app import db, celery
|
||||||
from flask_babel import _, lazy_gettext as _l # todo: set the locale based on account_id so that _() works
|
from flask_babel import _, lazy_gettext as _l # todo: set the locale based on account_id so that _() works
|
||||||
import boto3
|
import boto3
|
||||||
from botocore.exceptions import ClientError
|
from botocore.exceptions import ClientError
|
||||||
|
@ -9,6 +9,7 @@ AWS_REGION = "ap-southeast-2"
|
||||||
CHARSET = "UTF-8"
|
CHARSET = "UTF-8"
|
||||||
|
|
||||||
|
|
||||||
|
@celery.task
|
||||||
def send_async_email(subject, sender, recipients, text_body, html_body, reply_to):
|
def send_async_email(subject, sender, recipients, text_body, html_body, reply_to):
|
||||||
if type(recipients) == str:
|
if type(recipients) == str:
|
||||||
recipients = [recipients]
|
recipients = [recipients]
|
||||||
|
@ -62,5 +63,7 @@ def send_async_email(subject, sender, recipients, text_body, html_body, reply_to
|
||||||
|
|
||||||
|
|
||||||
def send_email(subject, sender, recipients: List[str], text_body, html_body, reply_to=None):
|
def send_email(subject, sender, recipients: List[str], text_body, html_body, reply_to=None):
|
||||||
# todo: make async or threaded
|
if current_app.debug:
|
||||||
send_async_email(subject, sender, recipients, text_body, html_body, reply_to)
|
send_async_email(subject, sender, recipients, text_body, html_body, reply_to)
|
||||||
|
else:
|
||||||
|
send_async_email.delay(subject, sender, recipients, text_body, html_body, reply_to)
|
||||||
|
|
|
@ -107,6 +107,7 @@ def verification_warning():
|
||||||
flash(_('Please click the link in your email inbox to verify your account.'), 'warning')
|
flash(_('Please click the link in your email inbox to verify your account.'), 'warning')
|
||||||
|
|
||||||
|
|
||||||
|
@cache.cached(timeout=6)
|
||||||
def activitypub_application():
|
def activitypub_application():
|
||||||
application_data = {
|
application_data = {
|
||||||
'@context': default_context(),
|
'@context': default_context(),
|
||||||
|
|
|
@ -488,6 +488,16 @@ fieldset legend {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.render_username {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
.render_username a img {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
border-radius: 50%;
|
||||||
|
vertical-align: bottom;
|
||||||
|
}
|
||||||
|
|
||||||
.comments > .comment {
|
.comments > .comment {
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
border-top: solid 1px #bbb;
|
border-top: solid 1px #bbb;
|
||||||
|
|
|
@ -198,6 +198,16 @@ nav, etc which are used site-wide */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.render_username {
|
||||||
|
display: inline;
|
||||||
|
a img {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
border-radius: 50%;
|
||||||
|
vertical-align: bottom;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.comments > .comment {
|
.comments > .comment {
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
border-top: solid 1px $grey;
|
border-top: solid 1px $grey;
|
||||||
|
|
|
@ -1,12 +1,18 @@
|
||||||
{% macro render_username(user) %}
|
{% macro render_username(user) %}
|
||||||
{% if user.deleted %}
|
<span class="render_username">
|
||||||
|
{% if user.deleted %}
|
||||||
[deleted]
|
[deleted]
|
||||||
{% else %}
|
{% else %}
|
||||||
|
{% if user.avatar_id %}
|
||||||
|
<a href="/u/{{ user.link() }}" title="{{ user.ap_id if user.ap_id != none else user.user_name }}">
|
||||||
|
<img src="{{ user.avatar_image() }}" alt="Avatar" /></a>
|
||||||
|
{% endif %}
|
||||||
<a href="/u/{{ user.link() }}" title="{{ user.ap_id if user.ap_id != none else user.user_name }}">{{ user.user_name }}</a>
|
<a href="/u/{{ user.link() }}" title="{{ user.ap_id if user.ap_id != none else user.user_name }}">{{ user.user_name }}</a>
|
||||||
{% if user.created_recently() %}
|
{% if user.created_recently() %}
|
||||||
<span class="fe fe-new-account" title="New account"> </span>
|
<span class="fe fe-new-account" title="New account"> </span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
</span>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
|
|
@ -84,8 +84,8 @@
|
||||||
<h2>{{ _('About community') }}</h2>
|
<h2>{{ _('About community') }}</h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<p>{{ community.description|safe }}</p>
|
<p>{{ community.description_html|safe }}</p>
|
||||||
<p>{{ community.rules|safe }}</p>
|
<p>{{ community.rules_html|safe }}</p>
|
||||||
{% if len(mods) > 0 and not community.private_mods %}
|
{% if len(mods) > 0 and not community.private_mods %}
|
||||||
<h3>Moderators</h3>
|
<h3>Moderators</h3>
|
||||||
<ul>
|
<ul>
|
||||||
|
|
|
@ -134,8 +134,8 @@
|
||||||
<h2>{{ _('About community') }}</h2>
|
<h2>{{ _('About community') }}</h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<p>{{ post.community.description|safe }}</p>
|
<p>{{ post.community.description_html|safe }}</p>
|
||||||
<p>{{ post.community.rules|safe }}</p>
|
<p>{{ post.community.rules_html|safe }}</p>
|
||||||
{% if len(mods) > 0 and not post.community.private_mods %}
|
{% if len(mods) > 0 and not post.community.private_mods %}
|
||||||
<h3>Moderators</h3>
|
<h3>Moderators</h3>
|
||||||
<ol>
|
<ol>
|
||||||
|
|
|
@ -42,7 +42,6 @@ def render_template(template_name: str, **context) -> Response:
|
||||||
|
|
||||||
|
|
||||||
def request_etag_matches(etag):
|
def request_etag_matches(etag):
|
||||||
print(str(request.headers))
|
|
||||||
if 'If-None-Match' in request.headers:
|
if 'If-None-Match' in request.headers:
|
||||||
old_etag = request.headers['If-None-Match']
|
old_etag = request.headers['If-None-Match']
|
||||||
return old_etag == etag
|
return old_etag == etag
|
||||||
|
|
11
celery_worker.default.py
Normal file
11
celery_worker.default.py
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
import os
|
||||||
|
from app import celery, create_app
|
||||||
|
|
||||||
|
|
||||||
|
app = create_app()
|
||||||
|
if not app.debug:
|
||||||
|
os.environ['DATABASE_URL'] = 'postgresql+psycopg2://pyfedi:pyfedi@127.0.0.1/pyfedi'
|
||||||
|
os.environ['SERVER_NAME'] = 'piefed.ngrok.app'
|
||||||
|
|
||||||
|
app.app_context().push()
|
|
@ -27,4 +27,6 @@ class Config(object):
|
||||||
CACHE_DEFAULT_TIMEOUT = 300
|
CACHE_DEFAULT_TIMEOUT = 300
|
||||||
CACHE_THRESHOLD = 1000
|
CACHE_THRESHOLD = 1000
|
||||||
CACHE_KEY_PREFIX = 'pyfedi'
|
CACHE_KEY_PREFIX = 'pyfedi'
|
||||||
|
CELERY_BROKER_URL = os.environ.get('CELERY_BROKER_URL') or 'redis://localhost:6379/0'
|
||||||
|
RESULT_BACKEND = os.environ.get('RESULT_BACKEND') or 'redis://localhost:6379/0'
|
||||||
SQLALCHEMY_ECHO = False # set to true to see SQL in console
|
SQLALCHEMY_ECHO = False # set to true to see SQL in console
|
||||||
|
|
12
dev_notes.txt
Normal file
12
dev_notes.txt
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
for celery, run this:
|
||||||
|
|
||||||
|
celery -A celery_worker.celery worker --loglevel=INFO
|
||||||
|
|
||||||
|
|
||||||
|
on prod web server, celery is managed by systemd: /etc/default/celeryd and /etc/systemd/system/celeryd.service
|
||||||
|
|
||||||
|
sudo systemctl stop celeryd
|
||||||
|
sudo systemctl restart celeryd or sudo service celeryd restart
|
||||||
|
|
||||||
|
*** check for celery-related problems by looking in /var/log/celery ***
|
||||||
|
|
|
@ -144,7 +144,6 @@ environment
|
||||||
https://sh.itjust.works/c/sewingrepairing
|
https://sh.itjust.works/c/sewingrepairing
|
||||||
https://lemmy.world/c/fuckcars
|
https://lemmy.world/c/fuckcars
|
||||||
https://lemmy.world/c/evs
|
https://lemmy.world/c/evs
|
||||||
https://feddit.uk/c/evs
|
|
||||||
https://slrpnk.net/c/solarpunk
|
https://slrpnk.net/c/solarpunk
|
||||||
https://slrpnk.net/c/climate
|
https://slrpnk.net/c/climate
|
||||||
https://slrpnk.net/c/energy
|
https://slrpnk.net/c/energy
|
||||||
|
|
|
@ -26,3 +26,5 @@ Pillow
|
||||||
pillow-heif
|
pillow-heif
|
||||||
opengraph-parse=0.0.6
|
opengraph-parse=0.0.6
|
||||||
feedgen==0.9.0
|
feedgen==0.9.0
|
||||||
|
celery==5.3.6
|
||||||
|
redis==5.0.1
|
||||||
|
|
Loading…
Reference in a new issue