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 %} +
+{% endblock %} +{% endmacro %} + {% macro admin_announce_button(ap_object_id, permalink_id=None) %} {% block admin_announce_button scoped %}