Allow to delete webmentions
This commit is contained in:
parent
d692ec060f
commit
a435cd33c9
5 changed files with 77 additions and 1 deletions
37
app/admin.py
37
app/admin.py
|
@ -11,6 +11,7 @@ from fastapi.exceptions import HTTPException
|
||||||
from fastapi.responses import RedirectResponse
|
from fastapi.responses import RedirectResponse
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from sqlalchemy import and_
|
from sqlalchemy import and_
|
||||||
|
from sqlalchemy import delete
|
||||||
from sqlalchemy import func
|
from sqlalchemy import func
|
||||||
from sqlalchemy import or_
|
from sqlalchemy import or_
|
||||||
from sqlalchemy import select
|
from sqlalchemy import select
|
||||||
|
@ -883,6 +884,42 @@ async def admin_actions_force_delete(
|
||||||
return RedirectResponse(redirect_url, status_code=302)
|
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")
|
@router.post("/actions/follow")
|
||||||
async def admin_actions_follow(
|
async def admin_actions_follow(
|
||||||
request: Request,
|
request: Request,
|
||||||
|
|
14
app/boxes.py
14
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)
|
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(
|
async def _handle_delete_activity(
|
||||||
db_session: AsyncSession,
|
db_session: AsyncSession,
|
||||||
from_actor: models.Actor,
|
from_actor: models.Actor,
|
||||||
|
|
|
@ -142,6 +142,17 @@
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
|
{% macro admin_force_delete_webmention_button(webmention_id, permalink_id=None) %}
|
||||||
|
{% block admin_force_delete_webmention_button scoped %}
|
||||||
|
<form action="{{ request.url_for("admin_actions_force_delete_webmention") }}" class="object-delete-form" method="POST">
|
||||||
|
{{ embed_csrf_token() }}
|
||||||
|
{{ embed_redirect_url(permalink_id) }}
|
||||||
|
<input type="hidden" name="webmention_id" value="{{ webmention_id }}">
|
||||||
|
<input type="submit" value="local delete">
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
{% macro admin_announce_button(ap_object_id, permalink_id=None) %}
|
{% macro admin_announce_button(ap_object_id, permalink_id=None) %}
|
||||||
{% block admin_announce_button scoped %}
|
{% block admin_announce_button scoped %}
|
||||||
<form action="{{ request.url_for("admin_actions_announce") }}" method="POST">
|
<form action="{{ request.url_for("admin_actions_announce") }}" method="POST">
|
||||||
|
@ -473,6 +484,11 @@
|
||||||
<li>
|
<li>
|
||||||
<time class="dt-published" datetime="{{ wm_reply.published_at.replace(microsecond=0).isoformat() }}" title="{{ wm_reply.published_at.replace(microsecond=0).isoformat() }}">{{ wm_reply.published_at | timeago }}</time>
|
<time class="dt-published" datetime="{{ wm_reply.published_at.replace(microsecond=0).isoformat() }}" title="{{ wm_reply.published_at.replace(microsecond=0).isoformat() }}">{{ wm_reply.published_at | timeago }}</time>
|
||||||
</li>
|
</li>
|
||||||
|
{% if is_admin %}
|
||||||
|
<li>
|
||||||
|
{{ admin_force_delete_webmention_button(wm_reply.webmention_id) }}
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -125,6 +125,7 @@ class WebmentionReply:
|
||||||
url: str
|
url: str
|
||||||
published_at: datetime.datetime
|
published_at: datetime.datetime
|
||||||
in_reply_to: str
|
in_reply_to: str
|
||||||
|
webmention_id: int
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_webmention(cls, webmention: Webmention) -> Optional["WebmentionReply"]:
|
def from_webmention(cls, webmention: Webmention) -> Optional["WebmentionReply"]:
|
||||||
|
@ -147,6 +148,7 @@ class WebmentionReply:
|
||||||
item["properties"]["published"][0]
|
item["properties"]["published"][0]
|
||||||
).replace(tzinfo=None),
|
).replace(tzinfo=None),
|
||||||
in_reply_to=webmention.target, # type: ignore
|
in_reply_to=webmention.target, # type: ignore
|
||||||
|
webmention_id=webmention.id, # type: ignore
|
||||||
)
|
)
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception(
|
logger.exception(
|
||||||
|
|
|
@ -8,6 +8,7 @@ from fastapi import HTTPException
|
||||||
from fastapi import Request
|
from fastapi import Request
|
||||||
from fastapi.responses import JSONResponse
|
from fastapi.responses import JSONResponse
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
from sqlalchemy import func
|
||||||
from sqlalchemy import select
|
from sqlalchemy import select
|
||||||
|
|
||||||
from app import models
|
from app import models
|
||||||
|
@ -204,7 +205,13 @@ async def _handle_webmention_side_effects(
|
||||||
) -> None:
|
) -> None:
|
||||||
if webmention.webmention_type == models.WebmentionType.UNKNOWN:
|
if webmention.webmention_type == models.WebmentionType.UNKNOWN:
|
||||||
# TODO: recount everything
|
# 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:
|
elif webmention.webmention_type == models.WebmentionType.LIKE:
|
||||||
mentioned_object.likes_count = await _get_outbox_likes_count(
|
mentioned_object.likes_count = await _get_outbox_likes_count(
|
||||||
db_session, mentioned_object
|
db_session, mentioned_object
|
||||||
|
|
Loading…
Reference in a new issue