refresh stale user profiles

This commit is contained in:
rimu 2023-12-27 15:47:17 +13:00
parent 6ac8aca227
commit 71289b9435
3 changed files with 51 additions and 6 deletions

View file

@ -2,6 +2,7 @@ from __future__ import annotations
import json import json
import os import os
from datetime import timedelta
from random import randint from random import randint
from typing import Union, Tuple from typing import Union, Tuple
from flask import current_app, request, g from flask import current_app, request, g
@ -205,6 +206,8 @@ def find_actor_or_create(actor: str) -> Union[User, Community, None]:
user = Community.query.filter_by(ap_profile_id=actor).first() user = Community.query.filter_by(ap_profile_id=actor).first()
if user is not None: if user is not None:
if not user.is_local() and user.ap_fetched_at < utcnow() - timedelta(days=7):
refresh_user_profile(user.id)
return user return user
else: # User does not exist in the DB, it's going to need to be created from it's remote home instance else: # User does not exist in the DB, it's going to need to be created from it's remote home instance
if actor.startswith('https://'): if actor.startswith('https://'):
@ -247,11 +250,53 @@ def extract_domain_and_actor(url_string: str):
return server_domain, actor return server_domain, actor
def refresh_user_profile(user_id):
if current_app.debug:
refresh_user_profile_task(user_id)
else:
refresh_user_profile_task.apply_async(args=(user_id), countdown=randint(1, 10))
@celery.task
def refresh_user_profile_task(user_id):
user = User.query.get(user_id)
if user:
actor_data = get_request(user.ap_profile_id, headers={'Accept': 'application/activity+json'})
if actor_data.status_code == 200:
activity_json = actor_data.json()
actor_data.close()
user.user_name = activity_json['preferredUsername']
user.about_html = parse_summary(activity_json)
user.ap_fetched_at = utcnow()
user.public_key=activity_json['publicKey']['publicKeyPem']
avatar_changed = cover_changed = False
if 'icon' in activity_json:
if activity_json['icon']['url'] != user.avatar.source_url:
user.avatar.delete_from_disk()
avatar = File(source_url=activity_json['icon']['url'])
user.avatar = avatar
db.session.add(avatar)
avatar_changed = True
if 'image' in activity_json:
if activity_json['image']['url'] != user.cover.source_url:
user.cover.delete_from_disk()
cover = File(source_url=activity_json['image']['url'])
user.cover = cover
db.session.add(cover)
cover_changed = True
db.session.commit()
if user.avatar_id and avatar_changed:
make_image_sizes(user.avatar_id, 40, 250, 'users')
if user.cover_id and cover_changed:
make_image_sizes(user.cover_id, 700, 1600, 'users')
def actor_json_to_model(activity_json, address, server): def actor_json_to_model(activity_json, address, server):
if activity_json['type'] == 'Person': if activity_json['type'] == 'Person':
user = User(user_name=activity_json['preferredUsername'], user = User(user_name=activity_json['preferredUsername'],
email=f"{address}@{server}", email=f"{address}@{server}",
about=parse_summary(activity_json), about_html=parse_summary(activity_json),
created=activity_json['published'] if 'published' in activity_json else utcnow(), created=activity_json['published'] if 'published' in activity_json else utcnow(),
ap_id=f"{address}@{server}", ap_id=f"{address}@{server}",
ap_public_url=activity_json['id'], ap_public_url=activity_json['id'],

View file

@ -1,7 +1,7 @@
from sqlalchemy.sql.operators import or_ from sqlalchemy.sql.operators import or_
from app import db, cache from app import db, cache
from app.activitypub.util import default_context, make_image_sizes_async from app.activitypub.util import default_context, make_image_sizes_async, refresh_user_profile
from app.constants import SUBSCRIPTION_PENDING, SUBSCRIPTION_MEMBER, POST_TYPE_IMAGE, POST_TYPE_LINK, SUBSCRIPTION_OWNER from app.constants import SUBSCRIPTION_PENDING, SUBSCRIPTION_MEMBER, POST_TYPE_IMAGE, POST_TYPE_LINK, SUBSCRIPTION_OWNER
from app.main import bp from app.main import bp
from flask import g, session, flash, request, current_app, url_for, redirect, make_response, jsonify from flask import g, session, flash, request, current_app, url_for, redirect, make_response, jsonify
@ -99,9 +99,7 @@ def robots():
@bp.route('/test') @bp.route('/test')
def test(): def test():
make_image_sizes_async(159, 60, 250, 'communities') refresh_user_profile(12)
make_image_sizes_async(140, 60, 250, 'communities')
make_image_sizes_async(141, 700, 1600, 'communities')
return 'done' return 'done'

View file

@ -278,7 +278,7 @@ class User(UserMixin, db.Model):
search_vector = db.Column(TSVectorType('user_name', 'bio', 'keywords')) search_vector = db.Column(TSVectorType('user_name', 'bio', 'keywords'))
activity = db.relationship('ActivityLog', backref='account', lazy='dynamic', cascade="all, delete-orphan") activity = db.relationship('ActivityLog', backref='account', lazy='dynamic', cascade="all, delete-orphan")
posts = db.relationship('Post', lazy='dynamic', cascade="all, delete-orphan") posts = db.relationship('Post', lazy='dynamic', cascade="all, delete-orphan")
post_replies = db.relationship('PostReply', backref='author', lazy='dynamic', cascade="all, delete-orphan") post_replies = db.relationship('PostReply', lazy='dynamic', cascade="all, delete-orphan")
roles = db.relationship('Role', secondary=user_role, lazy='dynamic', cascade="all, delete") roles = db.relationship('Role', secondary=user_role, lazy='dynamic', cascade="all, delete")
@ -586,6 +586,8 @@ class PostReply(db.Model):
search_vector = db.Column(TSVectorType('body')) search_vector = db.Column(TSVectorType('body'))
author = db.relationship('User', lazy='joined', foreign_keys=[user_id], single_parent=True)
def is_local(self): def is_local(self):
return self.ap_id is None or self.ap_id.startswith('https://' + current_app.config['SERVER_NAME']) return self.ap_id is None or self.ap_id.startswith('https://' + current_app.config['SERVER_NAME'])