diff options
| author | Dennis Fink | 2026-08-15 20:54:53 +0200 |
|---|---|---|
| committer | Dennis Fink | 2026-08-15 20:54:53 +0200 |
| commit | e0f65d7ff582b32f1c7b5508bba8cda807aaae5f (patch) | |
| tree | 6693b9fc259bf642e0392b4d8920fbca7ffd6dda /webmentions_ssg/views.py | |
| parent | b3e12b975064f0873bdb4d2834cae75925387d6b (diff) | |
| download | webmentions-ssg-e0f65d7ff582b32f1c7b5508bba8cda807aaae5f.tar.gz webmentions-ssg-e0f65d7ff582b32f1c7b5508bba8cda807aaae5f.zip | |
feat(sender): add outgoing Webmention pipeline
Scan generated h-entry documents for Webmention targets and track source
revisions and delivery state in the database.
Discover target endpoints, send Webmentions asynchronously, and reconcile
updated, removed, restored, and deleted sources across scans.
Add authenticated views for inspecting sent Webmentions and make the scanner
schedule configurable.
Diffstat (limited to '')
| -rw-r--r-- | webmentions_ssg/views.py | 85 |
1 files changed, 46 insertions, 39 deletions
diff --git a/webmentions_ssg/views.py b/webmentions_ssg/views.py index b52ae07..eeea765 100644 --- a/webmentions_ssg/views.py +++ b/webmentions_ssg/views.py @@ -16,10 +16,11 @@ from flask import ( ) from flask.typing import ResponseReturnValue from flask_login import current_user, login_required, login_user, logout_user +from sqlalchemy.orm import selectinload from . import CSRF, forms from . import DATABASE as db -from .models import ReceivedWebmention, User +from .models import ReceivedWebmention, Source, User from .tasks.receiver import verify_webmention root_page = Blueprint("root", __name__) @@ -80,18 +81,13 @@ def received() -> ResponseReturnValue: @root_page.post("/received/<uuid:identifier>/delete") @login_required -def delete_received_webmention( - identifier: uuid.UUID, -) -> ResponseReturnValue: +def delete_received_webmention(identifier: uuid.UUID) -> ResponseReturnValue: form = forms.AdminActionForm() if not form.validate_on_submit(): return abort(400) - webmention = db.session.get( - ReceivedWebmention, - identifier, - ) + webmention = db.session.get(ReceivedWebmention, identifier) if webmention is None: return abort(404) @@ -102,27 +98,19 @@ def delete_received_webmention( flash(f"Webmention {webmention.uuid} deleted.", "success") return redirect( - url_for( - "root.received", - page=request.args.get("page", 1, type=int), - ) + url_for("root.received", page=request.args.get("page", 1, type=int)) ) @root_page.post("/received/<uuid:identifier>/reverify") @login_required -def reverify_received_webmention( - identifier: uuid.UUID, -) -> ResponseReturnValue: +def reverify_received_webmention(identifier: uuid.UUID) -> ResponseReturnValue: form = forms.AdminActionForm() if not form.validate_on_submit(): return abort(400) - webmention = db.session.get( - ReceivedWebmention, - identifier, - ) + webmention = db.session.get(ReceivedWebmention, identifier) if webmention is None: return abort(404) @@ -137,10 +125,41 @@ def reverify_received_webmention( flash(f"Webmention {webmention.uuid} queued for reverification.", "success") return redirect( - url_for( - "root.received", - page=request.args.get("page", 1, type=int), - ) + url_for("root.received", page=request.args.get("page", 1, type=int)) + ) + + +@root_page.route("/sent") +@login_required +def sent() -> ResponseReturnValue: + sources = db.paginate( + sa.select(Source) + .options(selectinload(Source.sent_webmentions)) + .order_by(Source.last_seen_at.desc()), + per_page=25, + ) + return render_template("sent.html", title="Sent Webmentions", sources=sources) + + +@root_page.route("/sent/<uuid:identifier>") +@login_required +def sent_source(identifier: uuid.UUID) -> ResponseReturnValue: + source = db.session.scalar( + sa.select(Source) + .options(selectinload(Source.sent_webmentions)) + .where(Source.uuid == identifier) + ) + if source is None: + return abort(404) + + webmentions = sorted( + source.sent_webmentions, key=lambda webmention: webmention.target + ) + return render_template( + "sent_source.html", + title="Sent Webmentions", + source=source, + webmentions=webmentions, ) @@ -157,19 +176,14 @@ def endpoint() -> ResponseReturnValue: webmention = db.session.execute( sa.select(ReceivedWebmention).where( - ReceivedWebmention.source == source, - ReceivedWebmention.target == target, + ReceivedWebmention.source == source, ReceivedWebmention.target == target ) ).scalar_one_or_none() if webmention is None: identifier = uuid.uuid7() - webmention = ReceivedWebmention( - uuid=identifier, - source=source, - target=target, - ) + webmention = ReceivedWebmention(uuid=identifier, source=source, target=target) db.session.add(webmention) @@ -204,16 +218,9 @@ def endpoint() -> ResponseReturnValue: verify_webmention(webmention.uuid) - status_url = url_for( - "root.status", - identifier=str(identifier), - _external=True, - ) + status_url = url_for("root.status", identifier=str(identifier), _external=True) - return Response( - status=201, - headers={"Location": status_url}, - ) + return Response(status=201, headers={"Location": status_url}) @root_page.route("/status/<uuid:identifier>") |
