lemoncurry/webmention/views.py

87 lines
3.1 KiB
Python
Raw Normal View History

from django.http import HttpResponse
from django.urls import resolve, reverse, Resolver404
from django.shortcuts import get_object_or_404
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_GET, require_POST
from entries.models import Entry
from lemoncurry.utils import bad_req
from urllib.parse import urljoin, urlparse
from .models import State, Webmention
@csrf_exempt
@require_POST
def accept(request):
2023-08-10 02:52:37 -04:00
if "source" not in request.POST:
return bad_req("missing source url")
source_url = request.POST["source"]
2023-08-10 02:52:37 -04:00
if "target" not in request.POST:
return bad_req("missing target url")
target_url = request.POST["target"]
source = urlparse(source_url)
target = urlparse(target_url)
2023-08-10 02:52:37 -04:00
if source.scheme not in ("http", "https"):
return bad_req("unsupported source scheme")
if target.scheme not in ("http", "https"):
return bad_req("unsupported target scheme")
if target.netloc != request.site.domain:
2023-08-10 02:52:37 -04:00
return bad_req("target not on this site")
origin = "https://" + target.netloc
try:
match = resolve(target.path)
except Resolver404:
2023-08-10 02:52:37 -04:00
return bad_req("target not found")
2023-08-10 02:52:37 -04:00
if match.view_name != "entries:entry":
return bad_req("target does not accept webmentions")
try:
2023-08-10 02:52:37 -04:00
entry = Entry.objects.get(pk=match.kwargs["id"])
except Entry.DoesNotExist:
2023-08-10 02:52:37 -04:00
return bad_req("target not found")
try:
mention = Webmention.objects.get(source=source_url, target=target_url)
except Webmention.DoesNotExist:
mention = Webmention()
mention.source = source_url
mention.target = target_url
mention.entry = entry
mention.state = State.PENDING
mention.save()
2023-08-10 02:52:37 -04:00
status_url = reverse("webmention:status", kwargs={"id": mention.id})
res = HttpResponse(status=201)
2023-08-10 02:52:37 -04:00
res["Location"] = urljoin(origin, status_url)
return res
@require_GET
def status(mention_id):
"""
Retrieve the verification status of the specified webmention.
A webmention is accepted synchronously by the target site (me, in this
case) and then must be verified asynchronously - this means contacting the
source URL and confirming that it really does link to the target URL it
claimed to. Until this verification is complete, we can't assume the
mention is real and shouldn't display it anywhere.
Therefore, when a webmention is accepted, we return a Location header
pointing to this endpoint. The source site may, if desired, use this
endpoint to check whether their webmention has been verified or not.
The status is currently returned as a plain string, e.g., 'pending'.
However, eventually this will use a nice template instead, possibly
displaying additional information about the mention. There's no
standardised format for the status response, and most implementations
currently use a friendly human-readable format, so I'll be doing the same.
"""
mention = get_object_or_404(Webmention.objects, pk=mention_id)
return HttpResponse(mention.get_state_display())