From a435cd33c92d2698cca1493388c0592cdcbbe2be Mon Sep 17 00:00:00 2001 From: Thomas Sileo Date: Sun, 20 Nov 2022 11:56:58 +0100 Subject: [PATCH] Allow to delete webmentions --- app/admin.py | 37 +++++++++++++++++++++++++++++++++++++ app/boxes.py | 14 ++++++++++++++ app/templates/utils.html | 16 ++++++++++++++++ app/utils/facepile.py | 2 ++ app/webmentions.py | 9 ++++++++- 5 files changed, 77 insertions(+), 1 deletion(-) diff --git a/app/admin.py b/app/admin.py index 7725803..e837c46 100644 --- a/app/admin.py +++ b/app/admin.py @@ -11,6 +11,7 @@ from fastapi.exceptions import HTTPException from fastapi.responses import RedirectResponse from loguru import logger from sqlalchemy import and_ +from sqlalchemy import delete from sqlalchemy import func from sqlalchemy import or_ from sqlalchemy import select @@ -883,6 +884,42 @@ async def admin_actions_force_delete( return RedirectResponse(redirect_url, status_code=302) +@router.post("/actions/force_delete_webmention") +async def admin_actions_force_delete_webmention( + request: Request, + webmention_id: int = Form(), + redirect_url: str = Form(), + csrf_check: None = Depends(verify_csrf_token), + db_session: AsyncSession = Depends(get_db_session), +) -> RedirectResponse: + webmention = await boxes.get_webmention_by_id(db_session, webmention_id) + if not webmention: + raise ValueError(f"Cannot find {webmention_id}") + if not webmention.outbox_object: + raise ValueError(f"Missing related outbox object for {webmention_id}") + + # TODO: move this + logger.info(f"Deleting {webmention_id}") + webmention.is_deleted = True + await db_session.flush() + from app.webmentions import _handle_webmention_side_effects + + await _handle_webmention_side_effects( + db_session, webmention, webmention.outbox_object + ) + # Delete related notifications + notif_deletion_result = await db_session.execute( + delete(models.Notification) + .where(models.Notification.webmention_id == webmention.id) + .execution_options(synchronize_session=False) + ) + logger.info( + f"Deleted {notif_deletion_result.rowcount} notifications" # type: ignore + ) + await db_session.commit() + return RedirectResponse(redirect_url, status_code=302) + + @router.post("/actions/follow") async def admin_actions_follow( request: Request, diff --git a/app/boxes.py b/app/boxes.py index 6667545..19a92f8 100644 --- a/app/boxes.py +++ b/app/boxes.py @@ -1088,6 +1088,20 @@ async def get_anybox_object_by_ap_id( return await get_inbox_object_by_ap_id(db_session, ap_id) +async def get_webmention_by_id( + db_session: AsyncSession, webmention_id: int +) -> models.Webmention | None: + return ( + await db_session.execute( + select(models.Webmention) + .where(models.Webmention.id == webmention_id) + .options( + joinedload(models.Webmention.outbox_object), + ) + ) + ).scalar_one_or_none() # type: ignore + + async def _handle_delete_activity( db_session: AsyncSession, from_actor: models.Actor, diff --git a/app/templates/utils.html b/app/templates/utils.html index 425a5e5..2cf460a 100644 --- a/app/templates/utils.html +++ b/app/templates/utils.html @@ -142,6 +142,17 @@ {% endblock %} {% endmacro %} +{% macro admin_force_delete_webmention_button(webmention_id, permalink_id=None) %} +{% block admin_force_delete_webmention_button scoped %} +
+ {{ embed_csrf_token() }} + {{ embed_redirect_url(permalink_id) }} + + +
+{% endblock %} +{% endmacro %} + {% macro admin_announce_button(ap_object_id, permalink_id=None) %} {% block admin_announce_button scoped %}
@@ -473,6 +484,11 @@
  • + {% if is_admin %} +
  • + {{ admin_force_delete_webmention_button(wm_reply.webmention_id) }} +
  • + {% endif %} diff --git a/app/utils/facepile.py b/app/utils/facepile.py index 57aede2..38f1e76 100644 --- a/app/utils/facepile.py +++ b/app/utils/facepile.py @@ -125,6 +125,7 @@ class WebmentionReply: url: str published_at: datetime.datetime in_reply_to: str + webmention_id: int @classmethod def from_webmention(cls, webmention: Webmention) -> Optional["WebmentionReply"]: @@ -147,6 +148,7 @@ class WebmentionReply: item["properties"]["published"][0] ).replace(tzinfo=None), in_reply_to=webmention.target, # type: ignore + webmention_id=webmention.id, # type: ignore ) except Exception: logger.exception( diff --git a/app/webmentions.py b/app/webmentions.py index ea9211f..4f2ab4c 100644 --- a/app/webmentions.py +++ b/app/webmentions.py @@ -8,6 +8,7 @@ from fastapi import HTTPException from fastapi import Request from fastapi.responses import JSONResponse from loguru import logger +from sqlalchemy import func from sqlalchemy import select from app import models @@ -204,7 +205,13 @@ async def _handle_webmention_side_effects( ) -> None: if webmention.webmention_type == models.WebmentionType.UNKNOWN: # TODO: recount everything - mentioned_object.webmentions_count = mentioned_object.webmentions_count + 1 + mentioned_object.webmentions_count = await db_session.scalar( + select(func.count(models.Webmention.id)).where( + models.Webmention.is_deleted.is_(False), + models.Webmention.outbox_object_id == mentioned_object.id, + models.Webmention.webmention_type == models.WebmentionType.UNKNOWN, + ) + ) elif webmention.webmention_type == models.WebmentionType.LIKE: mentioned_object.likes_count = await _get_outbox_likes_count( db_session, mentioned_object