mirror of
https://github.com/jointakahe/takahe.git
synced 2025-02-18 00:25:20 +00:00
Fix Accept header and supply actor outbox
Should help with Mitra among others. Refs #207.
This commit is contained in:
parent
ecde831e77
commit
3de188e406
7 changed files with 56 additions and 27 deletions
|
@ -107,16 +107,11 @@ class PostAdmin(admin.ModelAdmin):
|
||||||
list_display = ["id", "type", "author", "state", "created"]
|
list_display = ["id", "type", "author", "state", "created"]
|
||||||
list_filter = ("type", "local", "visibility", "state", "created")
|
list_filter = ("type", "local", "visibility", "state", "created")
|
||||||
raw_id_fields = ["to", "mentions", "author", "emojis"]
|
raw_id_fields = ["to", "mentions", "author", "emojis"]
|
||||||
actions = ["force_fetch", "reparse_hashtags"]
|
actions = ["reparse_hashtags"]
|
||||||
search_fields = ["content"]
|
search_fields = ["content"]
|
||||||
inlines = [PostAttachmentInline]
|
inlines = [PostAttachmentInline]
|
||||||
readonly_fields = ["created", "updated", "state_changed", "object_json"]
|
readonly_fields = ["created", "updated", "state_changed", "object_json"]
|
||||||
|
|
||||||
@admin.action(description="Force Fetch")
|
|
||||||
def force_fetch(self, request, queryset):
|
|
||||||
for instance in queryset:
|
|
||||||
instance.debug_fetch()
|
|
||||||
|
|
||||||
@admin.action(description="Reprocess content for hashtags")
|
@admin.action(description="Reprocess content for hashtags")
|
||||||
def reparse_hashtags(self, request, queryset):
|
def reparse_hashtags(self, request, queryset):
|
||||||
for instance in queryset:
|
for instance in queryset:
|
||||||
|
|
|
@ -5,7 +5,6 @@ from typing import Optional
|
||||||
import httpx
|
import httpx
|
||||||
import urlman
|
import urlman
|
||||||
from asgiref.sync import async_to_sync, sync_to_async
|
from asgiref.sync import async_to_sync, sync_to_async
|
||||||
from django.conf import settings
|
|
||||||
from django.contrib.postgres.indexes import GinIndex
|
from django.contrib.postgres.indexes import GinIndex
|
||||||
from django.db import models, transaction
|
from django.db import models, transaction
|
||||||
from django.template import loader
|
from django.template import loader
|
||||||
|
@ -874,24 +873,6 @@ class Post(StatorModel):
|
||||||
raise ValueError("Actor on delete does not match object")
|
raise ValueError("Actor on delete does not match object")
|
||||||
post.delete()
|
post.delete()
|
||||||
|
|
||||||
def debug_fetch(self):
|
|
||||||
"""
|
|
||||||
Fetches the Post from its original URL again and updates us with it
|
|
||||||
"""
|
|
||||||
response = httpx.get(
|
|
||||||
self.object_uri,
|
|
||||||
headers={
|
|
||||||
"Accept": "application/json",
|
|
||||||
"User-Agent": settings.TAKAHE_USER_AGENT,
|
|
||||||
},
|
|
||||||
follow_redirects=True,
|
|
||||||
)
|
|
||||||
if 200 <= response.status_code < 300:
|
|
||||||
return self.by_ap(
|
|
||||||
canonicalise(response.json(), include_security=True),
|
|
||||||
update=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
### Mastodon API ###
|
### Mastodon API ###
|
||||||
|
|
||||||
def to_mastodon_json(self, interactions=None):
|
def to_mastodon_json(self, interactions=None):
|
||||||
|
|
|
@ -200,7 +200,9 @@ class HttpSignature:
|
||||||
body_bytes = b""
|
body_bytes = b""
|
||||||
# GET requests get implicit accept headers added
|
# GET requests get implicit accept headers added
|
||||||
if method == "get":
|
if method == "get":
|
||||||
headers["Accept"] = "application/activity+json"
|
headers[
|
||||||
|
"Accept"
|
||||||
|
] = 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'
|
||||||
# Sign the headers
|
# Sign the headers
|
||||||
signed_string = "\n".join(
|
signed_string = "\n".join(
|
||||||
f"{name.lower()}: {value}" for name, value in headers.items()
|
f"{name.lower()}: {value}" for name, value in headers.items()
|
||||||
|
|
|
@ -156,6 +156,7 @@ urlpatterns = [
|
||||||
# Identity views
|
# Identity views
|
||||||
path("@<handle>/", identity.ViewIdentity.as_view()),
|
path("@<handle>/", identity.ViewIdentity.as_view()),
|
||||||
path("@<handle>/inbox/", activitypub.Inbox.as_view()),
|
path("@<handle>/inbox/", activitypub.Inbox.as_view()),
|
||||||
|
path("@<handle>/outbox/", activitypub.Outbox.as_view()),
|
||||||
path("@<handle>/action/", identity.ActionIdentity.as_view()),
|
path("@<handle>/action/", identity.ActionIdentity.as_view()),
|
||||||
path("@<handle>/rss/", identity.IdentityFeed()),
|
path("@<handle>/rss/", identity.IdentityFeed()),
|
||||||
path("@<handle>/report/", report.SubmitReport.as_view()),
|
path("@<handle>/report/", report.SubmitReport.as_view()),
|
||||||
|
@ -233,6 +234,7 @@ urlpatterns = [
|
||||||
path("nodeinfo/2.0/", activitypub.NodeInfo2.as_view()),
|
path("nodeinfo/2.0/", activitypub.NodeInfo2.as_view()),
|
||||||
path("actor/", activitypub.SystemActorView.as_view()),
|
path("actor/", activitypub.SystemActorView.as_view()),
|
||||||
path("actor/inbox/", activitypub.Inbox.as_view()),
|
path("actor/inbox/", activitypub.Inbox.as_view()),
|
||||||
|
path("actor/outbox/", activitypub.EmptyOutbox.as_view()),
|
||||||
path("inbox/", activitypub.Inbox.as_view(), name="shared_inbox"),
|
path("inbox/", activitypub.Inbox.as_view(), name="shared_inbox"),
|
||||||
# API/Oauth
|
# API/Oauth
|
||||||
path("api/", api_router.urls),
|
path("api/", api_router.urls),
|
||||||
|
|
|
@ -335,6 +335,7 @@ class Identity(StatorModel):
|
||||||
"id": self.actor_uri,
|
"id": self.actor_uri,
|
||||||
"type": "Person",
|
"type": "Person",
|
||||||
"inbox": self.actor_uri + "inbox/",
|
"inbox": self.actor_uri + "inbox/",
|
||||||
|
"outbox": self.actor_uri + "outbox/",
|
||||||
"preferredUsername": self.username,
|
"preferredUsername": self.username,
|
||||||
"publicKey": {
|
"publicKey": {
|
||||||
"id": self.public_key_id,
|
"id": self.public_key_id,
|
||||||
|
|
|
@ -43,6 +43,7 @@ class SystemActor:
|
||||||
"id": self.actor_uri,
|
"id": self.actor_uri,
|
||||||
"type": "Application",
|
"type": "Application",
|
||||||
"inbox": self.actor_uri + "inbox/",
|
"inbox": self.actor_uri + "inbox/",
|
||||||
|
"outbox": self.actor_uri + "outbox/",
|
||||||
"endpoints": {
|
"endpoints": {
|
||||||
"sharedInbox": f"https://{settings.MAIN_DOMAIN}/inbox/",
|
"sharedInbox": f"https://{settings.MAIN_DOMAIN}/inbox/",
|
||||||
},
|
},
|
||||||
|
|
|
@ -2,7 +2,7 @@ import json
|
||||||
|
|
||||||
from asgiref.sync import async_to_sync
|
from asgiref.sync import async_to_sync
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.http import HttpResponse, HttpResponseBadRequest, JsonResponse
|
from django.http import Http404, HttpResponse, HttpResponseBadRequest, JsonResponse
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
from django.views.decorators.csrf import csrf_exempt
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
from django.views.generic import View
|
from django.views.generic import View
|
||||||
|
@ -208,6 +208,53 @@ class Inbox(View):
|
||||||
return HttpResponse(status=202)
|
return HttpResponse(status=202)
|
||||||
|
|
||||||
|
|
||||||
|
class Outbox(View):
|
||||||
|
"""
|
||||||
|
The ActivityPub outbox for an identity
|
||||||
|
"""
|
||||||
|
|
||||||
|
def get(self, request, handle):
|
||||||
|
self.identity = by_handle_or_404(
|
||||||
|
self.request,
|
||||||
|
handle,
|
||||||
|
local=False,
|
||||||
|
fetch=True,
|
||||||
|
)
|
||||||
|
# If this not a local actor, 404
|
||||||
|
if not self.identity.local:
|
||||||
|
raise Http404("Not a local identity")
|
||||||
|
# Return an ordered collection with the most recent 10 public posts
|
||||||
|
posts = list(self.identity.posts.not_hidden().public()[:10])
|
||||||
|
return JsonResponse(
|
||||||
|
canonicalise(
|
||||||
|
{
|
||||||
|
"type": "OrderedCollection",
|
||||||
|
"totalItems": len(posts),
|
||||||
|
"orderedItems": [post.to_ap() for post in posts],
|
||||||
|
}
|
||||||
|
),
|
||||||
|
content_type="application/activity+json",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class EmptyOutbox(View):
|
||||||
|
"""
|
||||||
|
A fixed-empty outbox for the system actor
|
||||||
|
"""
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
return JsonResponse(
|
||||||
|
canonicalise(
|
||||||
|
{
|
||||||
|
"type": "OrderedCollection",
|
||||||
|
"totalItems": 0,
|
||||||
|
"orderedItems": [],
|
||||||
|
}
|
||||||
|
),
|
||||||
|
content_type="application/activity+json",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@method_decorator(cache_page(), name="dispatch")
|
@method_decorator(cache_page(), name="dispatch")
|
||||||
class SystemActorView(View):
|
class SystemActorView(View):
|
||||||
"""
|
"""
|
||||||
|
|
Loading…
Reference in a new issue