federate auto-deletes

This commit is contained in:
rimu 2024-08-16 16:10:09 +12:00
parent 8a8d79796e
commit 6f4ea028f7
3 changed files with 78 additions and 76 deletions

View file

@ -24,8 +24,9 @@ from app.email import send_verification_email, send_email
from app.models import Settings, BannedInstances, Interest, Role, User, RolePermission, Domain, ActivityPubLog, \
utcnow, Site, Instance, File, Notification, Post, CommunityMember, NotificationSubscription, PostReply, Language, \
Tag, InstanceRole, Community
from app.post.routes import post_delete_post
from app.utils import file_get_contents, retrieve_block_list, blocked_domains, retrieve_peertube_block_list, \
shorten_string, get_request, html_to_text, blocked_communities
shorten_string, get_request, html_to_text, blocked_communities, ap_datetime
def register(app):
@ -175,11 +176,10 @@ def register(app):
communities = Community.query.filter(Community.content_retention > 0).all()
for community in communities:
cut_off = utcnow() - timedelta(days=community.content_retention)
db.session.execute(text('UPDATE "post" SET deleted = true WHERE sticky = false AND posted_at < :cut_off AND community_id = :community_id'), {
'cut_off': cut_off,
'community_id': community.id
})
db.session.commit()
old_posts = Post.query.filter_by(sticky=False, community_id=community.id).filter(Post.posted_at < cut_off).all()
for post in old_posts:
post_delete_post(community, post, post.user_id)
community.post_count -= 1
# Remove activity older than 3 days
db.session.query(ActivityPubLog).filter(ActivityPubLog.created_at < utcnow() - timedelta(days=3)).delete()

View file

@ -713,7 +713,7 @@ def send_to_remote_instance_task(instance_id: int, community_id: int, payload):
community = Community.query.get(community_id)
if community:
instance = Instance.query.get(instance_id)
if instance.inbox:
if instance.inbox and not instance.dormant:
if post_request(instance.inbox, payload, community.private_key, community.ap_profile_id + '#main-key'):
instance.last_successful_send = utcnow()
instance.failures = 0

View file

@ -8,7 +8,7 @@ from flask_babel import _
from sqlalchemy import or_, desc
from wtforms import SelectField, RadioField
from app import db, constants, cache
from app import db, constants, cache, celery
from app.activitypub.signature import HttpSignature, post_request, default_context, post_request_in_background
from app.activitypub.util import notify_about_post_reply, inform_followers_of_post_update
from app.community.util import save_post, send_to_remote_instance
@ -1191,77 +1191,79 @@ def post_delete(post_id: int):
post = Post.query.get_or_404(post_id)
community = post.community
if post.user_id == current_user.id or community.is_moderator() or current_user.is_admin():
if post.url:
if post.cross_posts is not None:
old_cross_posts = Post.query.filter(Post.id.in_(post.cross_posts)).all()
post.cross_posts.clear()
for ocp in old_cross_posts:
if ocp.cross_posts is not None:
ocp.cross_posts.remove(post.id)
post.delete_dependencies()
post.deleted = True
g.site.last_active = community.last_active = utcnow()
db.session.commit()
flash(_('Post deleted.'))
delete_json = {
'id': f"https://{current_app.config['SERVER_NAME']}/activities/delete/{gibberish(15)}",
'type': 'Delete',
'actor': current_user.public_url(),
'audience': post.community.public_url(),
'to': [post.community.public_url(), 'https://www.w3.org/ns/activitystreams#Public'],
'published': ap_datetime(utcnow()),
'cc': [
current_user.followers_url()
],
'object': post.ap_id,
'uri': post.ap_id
}
if post.user_id != current_user.id:
delete_json['summary'] = 'Deleted by mod'
if not community.local_only:
if not post.community.is_local(): # this is a remote community, send it to the instance that hosts it
success = post_request(post.community.ap_inbox_url, delete_json, current_user.private_key,
current_user.public_url() + '#main-key')
if not success:
flash('Failed to send delete to remote server', 'error')
else: # local community - send it to followers on remote instances
announce = {
"id": f"https://{current_app.config['SERVER_NAME']}/activities/announce/{gibberish(15)}",
"type": 'Announce',
"to": [
"https://www.w3.org/ns/activitystreams#Public"
],
"actor": post.community.ap_profile_id,
"cc": [
post.community.ap_followers_url
],
'@context': default_context(),
'object': delete_json
}
for instance in post.community.following_instances():
if instance.inbox and not current_user.has_blocked_instance(instance.id) and not instance_banned(
instance.domain):
send_to_remote_instance(instance.id, post.community.id, announce)
followers = UserFollower.query.filter_by(local_user_id=post.user_id)
if followers:
instances = Instance.query.join(User, User.instance_id == Instance.id).join(UserFollower, UserFollower.remote_user_id == User.id)
instances = instances.filter(UserFollower.local_user_id == post.user_id)
for instance in instances:
if instance.inbox and not current_user.has_blocked_instance(instance.id) and not instance_banned(
instance.domain):
post_request_in_background(instance.inbox, delete_json, current_user.private_key, current_user.public_url() + '#main-key')
if post.user_id != current_user.id:
add_to_modlog('delete_post', community_id=community.id, link_text=shorten_string(post.title),
link=f'post/{post.id}')
post_delete_post(community, post, current_user.id)
return redirect(url_for('activitypub.community_profile', actor=community.ap_id if community.ap_id is not None else community.name))
def post_delete_post(community: Community, post: Post, user_id: int):
user: User = User.query.get(user_id)
if post.url:
if post.cross_posts is not None:
old_cross_posts = Post.query.filter(Post.id.in_(post.cross_posts)).all()
post.cross_posts.clear()
for ocp in old_cross_posts:
if ocp.cross_posts is not None:
ocp.cross_posts.remove(post.id)
post.delete_dependencies()
post.deleted = True
if hasattr(g, 'site'): # g.site is invalid when running from cli
g.site.last_active = community.last_active = utcnow()
flash(_('Post deleted.'))
db.session.commit()
delete_json = {
'id': f"https://{current_app.config['SERVER_NAME']}/activities/delete/{gibberish(15)}",
'type': 'Delete',
'actor': user.public_url(),
'audience': post.community.public_url(),
'to': [post.community.public_url(), 'https://www.w3.org/ns/activitystreams#Public'],
'published': ap_datetime(utcnow()),
'cc': [
user.followers_url()
],
'object': post.ap_id,
'uri': post.ap_id
}
if post.user_id != user.id:
delete_json['summary'] = 'Deleted by mod'
# Federation
if not community.local_only:
if not post.community.is_local(): # this is a remote community, send it to the instance that hosts it
post_request(post.community.ap_inbox_url, delete_json, user.private_key, user.public_url() + '#main-key')
else: # local community - send it to followers on remote instances
announce = {
"id": f"https://{current_app.config['SERVER_NAME']}/activities/announce/{gibberish(15)}",
"type": 'Announce',
"to": [
"https://www.w3.org/ns/activitystreams#Public"
],
"actor": post.community.ap_profile_id,
"cc": [
post.community.ap_followers_url
],
'@context': default_context(),
'object': delete_json
}
for instance in post.community.following_instances():
if instance.inbox and not user.has_blocked_instance(instance.id) and not instance_banned(instance.domain):
send_to_remote_instance(instance.id, post.community.id, announce)
# Federate to microblog followers
followers = UserFollower.query.filter_by(local_user_id=post.user_id)
if followers:
instances = Instance.query.join(User, User.instance_id == Instance.id).join(UserFollower,
UserFollower.remote_user_id == User.id)
instances = instances.filter(UserFollower.local_user_id == post.user_id)
for instance in instances:
if instance.inbox and not user.has_blocked_instance(instance.id) and not instance_banned(instance.domain) and not instance.dormant:
post_request_in_background(instance.inbox, delete_json, user.private_key, user.public_url() + '#main-key')
if post.user_id != user.id:
add_to_modlog('delete_post', community_id=community.id, link_text=shorten_string(post.title),
link=f'post/{post.id}')
@bp.route('/post/<int:post_id>/restore', methods=['GET', 'POST'])
@login_required
def post_restore(post_id: int):