federation: subscribe to remote community

This commit is contained in:
rimu 2023-11-17 22:02:44 +13:00
parent c9beb0c0da
commit d2a81ef76c
12 changed files with 71 additions and 18 deletions

View file

@ -419,7 +419,12 @@ def shared_inbox():
community = find_actor_or_create(community_ap_id)
if user and community:
member = CommunityMember.query.filter_by(user_id=user.id, community_id=community.id).first()
db.session.delete(member)
join_request = CommunityJoinRequest.query.filter_by(user_id=user.id,
community_id=community.id).first()
if member:
db.session.delete(member)
if join_request:
db.session.delete(join_request)
db.session.commit()
activity_log.result = 'success'
elif request_json['object']['type'] == 'Like': # Undoing an upvote

View file

@ -247,10 +247,10 @@ class HttpSignature:
key_id: str,
content_type: str = "application/json",
method: Literal["get", "post"] = "post",
timeout: int = 1,
timeout: int = 5,
):
"""
Performs an async request to the given path, with a document, signed
Performs a request to the given path, with a document, signed
as an identity.
"""
if "://" not in uri:
@ -265,6 +265,34 @@ class HttpSignature:
}
# If we have a body, add a digest and content type
if body is not None:
if '@context' not in body: # add a default json-ld context if necessary
body['@context'] = [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1",
{
"piefed": "https://piefed.social/ns#",
"lemmy": "https://join-lemmy.org/ns#",
"litepub": "http://litepub.social/ns#",
"pt": "https://joinpeertube.org/ns#",
"sc": "http://schema.org/",
"nsfl": "piefed:nsfl",
"ChatMessage": "litepub:ChatMessage",
"commentsEnabled": "pt:commentsEnabled",
"sensitive": "as:sensitive",
"matrixUserId": "lemmy:matrixUserId",
"postingRestrictedToMods": "lemmy:postingRestrictedToMods",
"removeData": "lemmy:removeData",
"stickied": "lemmy:stickied",
"moderators": {
"@type": "@id",
"@id": "lemmy:moderators"
},
"expires": "as:endTime",
"distinguished": "lemmy:distinguished",
"language": "sc:inLanguage",
"identifier": "sc:identifier"
}
]
body_bytes = json.dumps(body).encode("utf8")
headers["Digest"] = cls.calculate_digest(body_bytes)
headers["Content-Type"] = content_type
@ -298,8 +326,7 @@ class HttpSignature:
}
)
# Announce ourselves with an agent similar to Mastodon
headers["User-Agent"] = 'PieFed'
headers["User-Agent"] = 'PieFed/1.0'
# Send the request with all those headers except the pseudo one
del headers["(request-target)"]

View file

@ -201,7 +201,7 @@ def find_actor_or_create(actor: str) -> Union[User, Community, None]:
ap_profile_id=actor).first() # finds communities formatted like https://localhost/c/*
if current_app.config['SERVER_NAME'] + '/u/' in actor:
user = User.query.filter_by(username=actor.split('/')[-1], ap_id=None, banned=False).first() # finds local users
user = User.query.filter_by(user_name=actor.split('/')[-1], ap_id=None, banned=False).first() # finds local users
if user is None:
return None
elif actor.startswith('https://'):

View file

@ -28,7 +28,7 @@ class AddLocalCommunity(FlaskForm):
class SearchRemoteCommunity(FlaskForm):
address = StringField(_l('Server address'), validators=[DataRequired()])
address = StringField(_l('Community address'), render_kw={'placeholder': 'e.g. !name@server'}, validators=[DataRequired()])
submit = SubmitField(_l('Search'))

View file

@ -119,14 +119,14 @@ def subscribe(actor):
db.session.commit()
follow = {
"actor": f"https://{current_app.config['SERVER_NAME']}/u/{current_user.user_name}",
"to": [community.ap_id],
"object": community.ap_id,
"to": [community.ap_profile_id],
"object": community.ap_profile_id,
"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,
current_user.ap_profile_id + '#main-key')
current_user.profile_id() + '#main-key')
if message.status_code == 200:
flash('Your request to subscribe has been sent to ' + community.title)
else:

View file

@ -264,6 +264,9 @@ class User(UserMixin, db.Model):
return Community.query.filter(Community.banned == False).\
join(CommunityMember).filter(CommunityMember.is_banned == False).all()
def profile_id(self):
return self.ap_profile_id if self.ap_profile_id else f"{self.user_name}@{current_app.config['SERVER_NAME']}"
@staticmethod
def verify_reset_password_token(token):
try:

View file

@ -360,7 +360,7 @@ fieldset legend {
}
.post_list .post_teaser h3 {
font-size: 120%;
margin-top: 8px;
margin-top: 4px;
margin-bottom: 0;
}
.post_list .post_teaser .meta_row a, .post_list .post_teaser .main_row a, .post_list .post_teaser .utilities_row a {

View file

@ -121,7 +121,7 @@ nav, etc which are used site-wide */
h3 {
font-size: 120%;
margin-top: 8px;
margin-top: 4px;
margin-bottom: 0;
}

View file

@ -391,6 +391,14 @@ nav.navbar {
color: #777;
}
.communities_table tr td {
vertical-align: middle;
padding-top: 6px;
}
.communities_table tr td:first-child {
padding-top: 8px;
}
@media (prefers-color-scheme: dark) {
body {
background-color: #777;

View file

@ -165,6 +165,15 @@ nav.navbar {
}
}
.communities_table tr td {
vertical-align: middle;
padding-top: 6px;
&:first-child {
padding-top: 8px;
}
}
@media (prefers-color-scheme: dark) {
body {
background-color: $dark-grey;

View file

@ -7,6 +7,7 @@
<div class="card mt-5">
<div class="card-body p-6">
<div class="card-title">{{ _('Search') }}</div>
<p>Type address in the format !community@server.name. Search on <a href="https://lemmyverse.net/communities" target='_blank'>Lemmyverse.net</a> to find some.</p>
{{ render_form(form) }}
</div>
</div>
@ -22,12 +23,12 @@
<p>
<a href="/c/{{ new_community.link() }}"><img src="{{ new_community.icon_image()}}" class="community_icon rounded-circle" /></a>
<a href="/c/{{ new_community.link() }}">{{ new_community.title }}@{{ new_community.ap_domain }}</a>
{% if subscribed %}
<a class="btn btn-primary mt-4" href="/c/{{ new_community.link() }}/unsubscribe">{{ _('Unsubscribe') }}</a>
</p>
<p> {% if subscribed %}
<a class="btn btn-primary mt-4" href="/community/{{ new_community.link() }}/unsubscribe">{{ _('Unsubscribe') }}</a>
{% else %}
<a class="btn btn-primary mt-4" href="/c/{{ new_community.link() }}/subscribe">{{ _('Subscribe') }}</a>
<a class="btn btn-primary mt-4" href="/community/{{ new_community.link() }}/subscribe">{{ _('Subscribe') }}</a>
{% endif %}
</p>
</div>
</div>

View file

@ -31,7 +31,7 @@
</div>
{% if len(communities) > 0 %}
<div class="table-responsive-md">
<table class="table table-striped table-hover w-100">
<table class="communities_table table table-striped table-hover w-100">
<thead>
<tr>
<th scope="col" colspan="2">{{ _('Name') }}</th>
@ -45,7 +45,7 @@
{% for community in communities %}
<tr class="">
<td><a href="/c/{{ community.link() }}"><img src="{{ community.icon_image() }}" class="community_icon rounded-circle" loading="lazy" /></a></td>
<th scope="row"><a href="/c/{{ community.link() }}">{{ community.display_name() }}</a></th>
<td scope="row"><a href="/c/{{ community.link() }}">{{ community.display_name() }}</a></td>
<td>{{ community.post_count }}</td>
<td>{{ community.post_reply_count }}</td>
<td>{{ moment(community.last_active).fromNow(refresh=True) }}</td>