mirror of
https://codeberg.org/rimu/pyfedi
synced 2025-02-02 16:21:32 -08:00
log all outgoing actitivies
This commit is contained in:
parent
4a6492a15c
commit
fec2e9117c
7 changed files with 89 additions and 110 deletions
|
@ -192,6 +192,20 @@ def user_profile(actor):
|
|||
abort(404)
|
||||
|
||||
|
||||
@bp.route('/u/<actor>/outbox', methods=['GET'])
|
||||
def user_outbox(actor):
|
||||
outbox = {
|
||||
"@context": default_context(),
|
||||
'type': 'OrderedCollection',
|
||||
'id': f"https://{current_app.config['SERVER_NAME']}/u/{actor}/outbox",
|
||||
'orderedItems': [],
|
||||
'totalItems': 0
|
||||
}
|
||||
resp = jsonify(outbox)
|
||||
resp.content_type = 'application/activity+json'
|
||||
return resp
|
||||
|
||||
|
||||
@bp.route('/c/<actor>', methods=['GET'])
|
||||
def community_profile(actor):
|
||||
""" Requests to this endpoint can be for a JSON representation of the community, or a HTML rendering of it.
|
||||
|
@ -483,6 +497,7 @@ def shared_inbox():
|
|||
community.post_reply_count += 1
|
||||
community.last_active = utcnow()
|
||||
post.last_active = utcnow()
|
||||
post.reply_count += 1
|
||||
activity_log.result = 'success'
|
||||
db.session.commit()
|
||||
else:
|
||||
|
@ -915,6 +930,9 @@ def post_ap(post_id):
|
|||
def activities_json(type, id):
|
||||
activity = ActivityPubLog.query.filter_by(activity_id=f"https://{current_app.config['SERVER_NAME']}/activities/{type}/{id}").first()
|
||||
if activity:
|
||||
...
|
||||
activity_json = json.loads(activity.activity_json)
|
||||
resp = jsonify(activity_json)
|
||||
resp.content_type = 'application/activity+json'
|
||||
return resp
|
||||
else:
|
||||
abort(404)
|
||||
|
|
|
@ -44,9 +44,10 @@ from datetime import datetime
|
|||
from dateutil import parser
|
||||
from pyld import jsonld
|
||||
|
||||
from app import db
|
||||
from app.activitypub.util import default_context
|
||||
from app.constants import DATETIME_MS_FORMAT
|
||||
from app.models import utcnow
|
||||
from app.models import utcnow, ActivityPubLog
|
||||
|
||||
|
||||
def http_date(epoch_seconds=None):
|
||||
|
@ -73,6 +74,30 @@ def parse_ld_date(value: str | None) -> datetime | None:
|
|||
return None
|
||||
return parser.isoparse(value).replace(microsecond=0)
|
||||
|
||||
|
||||
def post_request(uri: str, body: dict | None, private_key: str, key_id: str, content_type: str = "application/activity+json",
|
||||
method: Literal["get", "post"] = "post", timeout: int = 5,):
|
||||
if '@context' not in body: # add a default json-ld context if necessary
|
||||
body['@context'] = default_context()
|
||||
log = ActivityPubLog(direction='out', activity_json=json.dumps(body),
|
||||
result='success', activity_id=body['id'])
|
||||
try:
|
||||
result = HttpSignature.signed_request(uri, body, private_key, key_id, content_type, method, timeout)
|
||||
if result.status_code != 200:
|
||||
log.result = 'failure'
|
||||
log.exception_message = f'Response status code was {result.status_code}'
|
||||
current_app.logger.error('Response code for post attempt was ' +
|
||||
str(result.status_code) + ' ' + result.text)
|
||||
except Exception as e:
|
||||
log.result = 'failure'
|
||||
log.exception_message='could not send:' + str(e)
|
||||
current_app.logger.error(f'Exception while sending post to {uri}')
|
||||
db.session.add(log)
|
||||
db.session.commit()
|
||||
|
||||
return log.result != 'failure'
|
||||
|
||||
|
||||
class VerificationError(BaseException):
|
||||
"""
|
||||
There was an error with verifying the signature
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
from datetime import datetime
|
||||
from typing import Union, Tuple
|
||||
from flask import current_app, request
|
||||
from sqlalchemy import text
|
||||
|
@ -143,36 +144,6 @@ def post_to_activity(post: Post, community: Community):
|
|||
return activity_data
|
||||
|
||||
|
||||
def validate_headers(headers, body):
|
||||
if headers['content-type'] != 'application/activity+json' and headers['content-type'] != 'application/ld+json':
|
||||
return False
|
||||
|
||||
if headers['user-agent'] in banned_user_agents():
|
||||
return False
|
||||
|
||||
if instance_blocked(headers['host']):
|
||||
return False
|
||||
|
||||
return validate_header_signature(body, headers['host'], headers['date'], headers['signature'])
|
||||
|
||||
|
||||
def validate_header_signature(body: str, host: str, date: str, signature: str) -> bool:
|
||||
body = json.loads(body)
|
||||
signature = parse_signature_header(signature)
|
||||
|
||||
key_domain = urlparse(signature['key_id']).hostname
|
||||
id_domain = urlparse(body['id']).hostname
|
||||
|
||||
if urlparse(body['object']['attributedTo']).hostname != key_domain:
|
||||
raise Exception('Invalid host url.')
|
||||
|
||||
if key_domain != id_domain:
|
||||
raise Exception('Wrong domain.')
|
||||
|
||||
user = find_actor_or_create(body['actor'])
|
||||
return verify_signature(user.private_key, signature, headers)
|
||||
|
||||
|
||||
def banned_user_agents():
|
||||
return [] # todo: finish this function
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ from flask_babel import _
|
|||
from sqlalchemy import or_, desc
|
||||
|
||||
from app import db, constants, cache
|
||||
from app.activitypub.signature import RsaKeys, HttpSignature
|
||||
from app.activitypub.signature import RsaKeys, HttpSignature, post_request
|
||||
from app.activitypub.util import default_context
|
||||
from app.community.forms import SearchRemoteCommunity, AddLocalCommunity, CreatePostForm, ReportCommunityForm, \
|
||||
DeleteCommunityForm
|
||||
|
@ -206,18 +206,12 @@ def subscribe(actor):
|
|||
"type": "Follow",
|
||||
"id": f"https://{current_app.config['SERVER_NAME']}/activities/follow/{join_request.id}"
|
||||
}
|
||||
try:
|
||||
message = HttpSignature.signed_request(community.ap_inbox_url, follow, current_user.private_key,
|
||||
success = post_request(community.ap_inbox_url, follow, current_user.private_key,
|
||||
current_user.profile_id() + '#main-key')
|
||||
if message.status_code == 200:
|
||||
flash('Your request to subscribe has been sent to ' + community.title)
|
||||
else:
|
||||
flash('Response status code was not 200', 'warning')
|
||||
current_app.logger.error('Response code for subscription attempt was ' +
|
||||
str(message.status_code) + ' ' + message.text)
|
||||
except Exception as ex:
|
||||
flash('Failed to send request to subscribe: ' + str(ex), 'error')
|
||||
current_app.logger.error("Exception while trying to subscribe" + str(ex))
|
||||
if success:
|
||||
flash('Your request to subscribe has been sent to ' + community.title)
|
||||
else:
|
||||
flash('There was a problem while trying to subscribe.', 'error')
|
||||
else: # for local communities, joining is instant
|
||||
banned = CommunityBan.query.filter_by(user_id=current_user.id, community_id=community.id).first()
|
||||
if banned:
|
||||
|
@ -261,18 +255,11 @@ def unsubscribe(actor):
|
|||
'id': f"https://{current_app.config['SERVER_NAME']}/activities/undo/" + gibberish(15),
|
||||
'object': follow
|
||||
}
|
||||
try:
|
||||
message = HttpSignature.signed_request(community.ap_inbox_url, undo, current_user.private_key,
|
||||
success = post_request(community.ap_inbox_url, undo, current_user.private_key,
|
||||
current_user.profile_id() + '#main-key')
|
||||
if message.status_code != 200:
|
||||
flash('Response status code was not 200', 'warning')
|
||||
current_app.logger.error('Response code for unsubscription attempt was ' +
|
||||
str(message.status_code) + ' ' + message.text)
|
||||
proceed = False
|
||||
except Exception as ex:
|
||||
proceed = False
|
||||
flash('Failed to send request to unsubscribe: ' + str(ex), 'error')
|
||||
current_app.logger.error("Exception while trying to unsubscribe" + str(ex))
|
||||
if not success:
|
||||
flash('There was a problem while trying to subscribe', 'error')
|
||||
|
||||
if proceed:
|
||||
db.session.query(CommunityMember).filter_by(user_id=current_user.id, community_id=community.id).delete()
|
||||
db.session.query(CommunityJoinRequest).filter_by(user_id=current_user.id, community_id=community.id).delete()
|
||||
|
@ -361,18 +348,12 @@ def add_post(actor):
|
|||
"audience": community.ap_profile_id,
|
||||
"object": page
|
||||
}
|
||||
try:
|
||||
message = HttpSignature.signed_request(community.ap_inbox_url, create, current_user.private_key,
|
||||
current_user.ap_profile_id + '#main-key')
|
||||
if message.status_code == 200:
|
||||
flash('Your post has been sent to ' + community.title)
|
||||
else:
|
||||
flash('Response status code was not 200', 'warning')
|
||||
current_app.logger.error('Response code for post attempt was ' +
|
||||
str(message.status_code) + ' ' + message.text)
|
||||
except Exception as ex:
|
||||
flash('Failed to send request to subscribe: ' + str(ex), 'error')
|
||||
current_app.logger.error("Exception while trying to subscribe" + str(ex))
|
||||
success = post_request(community.ap_inbox_url, create, current_user.private_key,
|
||||
current_user.ap_profile_id + '#main-key')
|
||||
if success:
|
||||
flash('Your post has been sent to ' + community.title)
|
||||
else:
|
||||
flash('There was a problem sending your post to ' + community.title)
|
||||
else: # local community - send post out to followers
|
||||
...
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ from flask_babel import _
|
|||
from sqlalchemy import or_, desc
|
||||
|
||||
from app import db, constants
|
||||
from app.activitypub.signature import HttpSignature
|
||||
from app.activitypub.signature import HttpSignature, post_request
|
||||
from app.activitypub.util import default_context
|
||||
from app.community.util import save_post
|
||||
from app.post.forms import NewReplyForm, ReportPostForm, MeaCulpaForm
|
||||
|
@ -104,16 +104,10 @@ def show_post(post_id: int):
|
|||
'id': f"https://{current_app.config['SERVER_NAME']}/activities/create/{gibberish(15)}"
|
||||
}
|
||||
|
||||
try:
|
||||
message = HttpSignature.signed_request(post.community.ap_inbox_url, create_json, current_user.private_key,
|
||||
success = post_request(post.community.ap_inbox_url, create_json, current_user.private_key,
|
||||
current_user.ap_profile_id + '#main-key')
|
||||
if message.status_code != 200:
|
||||
flash('Failed to send to remote instance', 'warning')
|
||||
current_app.logger.error('Response code for reply attempt was ' +
|
||||
str(message.status_code) + ' ' + message.text)
|
||||
except Exception as ex:
|
||||
flash('Failed to send request to subscribe: ' + str(ex), 'error')
|
||||
current_app.logger.error("Exception while trying to subscribe" + str(ex))
|
||||
if not success:
|
||||
flash('Failed to send to remote instance', 'error')
|
||||
else: # local community - send it to followers on remote instances
|
||||
...
|
||||
|
||||
|
@ -191,16 +185,10 @@ def post_vote(post_id: int, vote_direction):
|
|||
'id': f"https://{current_app.config['SERVER_NAME']}/activities/{action_type.lower()}/{gibberish(15)}",
|
||||
'audience': post.community.profile_id()
|
||||
}
|
||||
try:
|
||||
message = HttpSignature.signed_request(post.community.ap_inbox_url, action_json, current_user.private_key,
|
||||
success = post_request(post.community.ap_inbox_url, action_json, current_user.private_key,
|
||||
current_user.ap_profile_id + '#main-key')
|
||||
if message.status_code != 200:
|
||||
flash('Response status code was not 200', 'warning')
|
||||
current_app.logger.error('Response code for reply attempt was ' +
|
||||
str(message.status_code) + ' ' + message.text)
|
||||
except Exception as ex:
|
||||
flash('Failed to send reply: ' + str(ex), 'error')
|
||||
current_app.logger.error("Exception while trying to send reply" + str(ex))
|
||||
if not success:
|
||||
flash('Failed to send vote', 'warning')
|
||||
|
||||
current_user.last_seen = utcnow()
|
||||
db.session.commit()
|
||||
|
@ -266,16 +254,11 @@ def comment_vote(comment_id, vote_direction):
|
|||
'id': f"https://{current_app.config['SERVER_NAME']}/activities/{action_type.lower()}/{gibberish(15)}",
|
||||
'audience': comment.community.profile_id()
|
||||
}
|
||||
try:
|
||||
message = HttpSignature.signed_request(comment.community.ap_inbox_url, action_json, current_user.private_key,
|
||||
success = post_request(comment.community.ap_inbox_url, action_json, current_user.private_key,
|
||||
current_user.ap_profile_id + '#main-key')
|
||||
if message.status_code != 200:
|
||||
flash('Response status code was not 200', 'warning')
|
||||
current_app.logger.error('Response code for reply attempt was ' +
|
||||
str(message.status_code) + ' ' + message.text)
|
||||
except Exception as ex:
|
||||
flash('Failed to send reply: ' + str(ex), 'error')
|
||||
current_app.logger.error("Exception while trying to send reply" + str(ex))
|
||||
if not success:
|
||||
flash('Failed to send vote', 'error')
|
||||
|
||||
current_user.last_seen = utcnow()
|
||||
db.session.commit()
|
||||
comment.post.flush_cache()
|
||||
|
@ -388,16 +371,10 @@ def add_reply(post_id: int, comment_id: int):
|
|||
'type': 'Mention'
|
||||
}
|
||||
]
|
||||
try:
|
||||
message = HttpSignature.signed_request(post.community.ap_inbox_url, create_json, current_user.private_key,
|
||||
success = post_request(post.community.ap_inbox_url, create_json, current_user.private_key,
|
||||
current_user.ap_profile_id + '#main-key')
|
||||
if message.status_code != 200:
|
||||
flash('Response status code was not 200', 'warning')
|
||||
current_app.logger.error('Response code for reply attempt was ' +
|
||||
str(message.status_code) + ' ' + message.text)
|
||||
except Exception as ex:
|
||||
flash('Failed to send reply: ' + str(ex), 'error')
|
||||
current_app.logger.error("Exception while trying to send reply" + str(ex))
|
||||
if not success:
|
||||
flash('Failed to send reply', 'error')
|
||||
else: # local community - send it to followers on remote instances
|
||||
...
|
||||
if reply.depth <= constants.THREAD_CUTOFF_DEPTH:
|
||||
|
|
|
@ -25,9 +25,13 @@
|
|||
<td>{{ moment(activity.created_at).fromNow() }}</td>
|
||||
<td>{{ activity.direction }}</td>
|
||||
<td>{{ activity.activity_id }}</td>
|
||||
<td>{{ activity.activity_type }}</td>
|
||||
<td>{{ activity.result }}</td>
|
||||
<td>{{ activity.exception_message }}</td>
|
||||
<td>{{ activity.activity_type if activity.activity_type else '' }}</td>
|
||||
{% if activity.result == 'success' %}
|
||||
<td><span style="color: green;">{{ activity.result }}</span></td>
|
||||
{% else %}
|
||||
<td><span style="color: red;">{{ activity.result }}</span></td>
|
||||
{% endif %}
|
||||
<td>{{ activity.exception_message if activity.exception_message else '' }}</td>
|
||||
<td><pre> </pre></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import random
|
||||
from datetime import datetime
|
||||
from typing import List
|
||||
from typing import List, Literal
|
||||
|
||||
import markdown2
|
||||
import math
|
||||
|
@ -18,7 +20,8 @@ from sqlalchemy import text
|
|||
from wtforms.fields import SelectField, SelectMultipleField
|
||||
from wtforms.widgets import Select, html_params, ListWidget, CheckboxInput
|
||||
from app import db, cache
|
||||
from app.models import Settings, Domain, Instance, BannedInstances, User, Community, DomainBlock
|
||||
|
||||
from app.models import Settings, Domain, Instance, BannedInstances, User, Community, DomainBlock, ActivityPubLog
|
||||
|
||||
|
||||
# Flask's render_template function, with support for themes added
|
||||
|
|
Loading…
Add table
Reference in a new issue