forked from forks/microblog.pub
Switch to aiosqlite
This commit is contained in:
parent
18bd2cb664
commit
1f54a6a6ac
21 changed files with 698 additions and 549 deletions
40
app/actor.py
40
app/actor.py
|
@ -4,11 +4,11 @@ from typing import Union
|
|||
from urllib.parse import urlparse
|
||||
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy.orm import joinedload
|
||||
|
||||
from app import activitypub as ap
|
||||
from app import media
|
||||
from app.database import AsyncSession
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from app.models import Actor as ActorModel
|
||||
|
@ -131,7 +131,7 @@ class RemoteActor(Actor):
|
|||
LOCAL_ACTOR = RemoteActor(ap_actor=ap.ME)
|
||||
|
||||
|
||||
def save_actor(db: Session, ap_actor: ap.RawObject) -> "ActorModel":
|
||||
async def save_actor(db_session: AsyncSession, ap_actor: ap.RawObject) -> "ActorModel":
|
||||
from app import models
|
||||
|
||||
if ap_type := ap_actor.get("type") not in ap.ACTOR_TYPES:
|
||||
|
@ -143,23 +143,25 @@ def save_actor(db: Session, ap_actor: ap.RawObject) -> "ActorModel":
|
|||
ap_type=ap_actor["type"],
|
||||
handle=_handle(ap_actor),
|
||||
)
|
||||
db.add(actor)
|
||||
db.commit()
|
||||
db.refresh(actor)
|
||||
db_session.add(actor)
|
||||
await db_session.commit()
|
||||
await db_session.refresh(actor)
|
||||
return actor
|
||||
|
||||
|
||||
def fetch_actor(db: Session, actor_id: str) -> "ActorModel":
|
||||
async def fetch_actor(db_session: AsyncSession, actor_id: str) -> "ActorModel":
|
||||
from app import models
|
||||
|
||||
existing_actor = db.execute(
|
||||
select(models.Actor).where(models.Actor.ap_id == actor_id)
|
||||
).scalar_one_or_none()
|
||||
existing_actor = (
|
||||
await db_session.scalars(
|
||||
select(models.Actor).where(models.Actor.ap_id == actor_id)
|
||||
)
|
||||
).one_or_none()
|
||||
if existing_actor:
|
||||
return existing_actor
|
||||
|
||||
ap_actor = ap.get(actor_id)
|
||||
return save_actor(db, ap_actor)
|
||||
return await save_actor(db_session, ap_actor)
|
||||
|
||||
|
||||
@dataclass
|
||||
|
@ -175,8 +177,8 @@ class ActorMetadata:
|
|||
ActorsMetadata = dict[str, ActorMetadata]
|
||||
|
||||
|
||||
def get_actors_metadata(
|
||||
db: Session,
|
||||
async def get_actors_metadata(
|
||||
db_session: AsyncSession,
|
||||
actors: list[Union["ActorModel", "RemoteActor"]],
|
||||
) -> ActorsMetadata:
|
||||
from app import models
|
||||
|
@ -184,17 +186,19 @@ def get_actors_metadata(
|
|||
ap_actor_ids = [actor.ap_id for actor in actors]
|
||||
followers = {
|
||||
follower.ap_actor_id: follower.inbox_object.ap_id
|
||||
for follower in db.scalars(
|
||||
select(models.Follower)
|
||||
.where(models.Follower.ap_actor_id.in_(ap_actor_ids))
|
||||
.options(joinedload(models.Follower.inbox_object))
|
||||
for follower in (
|
||||
await db_session.scalars(
|
||||
select(models.Follower)
|
||||
.where(models.Follower.ap_actor_id.in_(ap_actor_ids))
|
||||
.options(joinedload(models.Follower.inbox_object))
|
||||
)
|
||||
)
|
||||
.unique()
|
||||
.all()
|
||||
}
|
||||
following = {
|
||||
following.ap_actor_id
|
||||
for following in db.execute(
|
||||
for following in await db_session.execute(
|
||||
select(models.Following.ap_actor_id).where(
|
||||
models.Following.ap_actor_id.in_(ap_actor_ids)
|
||||
)
|
||||
|
@ -202,7 +206,7 @@ def get_actors_metadata(
|
|||
}
|
||||
sent_follow_requests = {
|
||||
follow_req.ap_object["object"]: follow_req.ap_id
|
||||
for follow_req in db.execute(
|
||||
for follow_req in await db_session.execute(
|
||||
select(models.OutboxObject.ap_object, models.OutboxObject.ap_id).where(
|
||||
models.OutboxObject.ap_type == "Follow",
|
||||
models.OutboxObject.undone_by_outbox_object_id.is_(None),
|
||||
|
|
285
app/admin.py
285
app/admin.py
|
@ -8,7 +8,6 @@ from fastapi.exceptions import HTTPException
|
|||
from fastapi.responses import RedirectResponse
|
||||
from sqlalchemy import func
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy.orm import joinedload
|
||||
|
||||
from app import activitypub as ap
|
||||
|
@ -25,7 +24,8 @@ from app.config import generate_csrf_token
|
|||
from app.config import session_serializer
|
||||
from app.config import verify_csrf_token
|
||||
from app.config import verify_password
|
||||
from app.database import get_db
|
||||
from app.database import AsyncSession
|
||||
from app.database import get_db_session
|
||||
from app.lookup import lookup
|
||||
from app.uploads import save_upload
|
||||
from app.utils import pagination
|
||||
|
@ -62,30 +62,36 @@ unauthenticated_router = APIRouter()
|
|||
|
||||
|
||||
@router.get("/")
|
||||
def admin_index(
|
||||
async def admin_index(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
db_session: AsyncSession = Depends(get_db_session),
|
||||
) -> templates.TemplateResponse:
|
||||
return templates.render_template(db, request, "index.html", {"request": request})
|
||||
return await templates.render_template(
|
||||
db_session, request, "index.html", {"request": request}
|
||||
)
|
||||
|
||||
|
||||
@router.get("/lookup")
|
||||
def get_lookup(
|
||||
async def get_lookup(
|
||||
request: Request,
|
||||
query: str | None = None,
|
||||
db: Session = Depends(get_db),
|
||||
db_session: AsyncSession = Depends(get_db_session),
|
||||
) -> templates.TemplateResponse:
|
||||
ap_object = None
|
||||
actors_metadata = {}
|
||||
if query:
|
||||
ap_object = lookup(db, query)
|
||||
ap_object = await lookup(db_session, query)
|
||||
if ap_object.ap_type in ap.ACTOR_TYPES:
|
||||
actors_metadata = get_actors_metadata(db, [ap_object]) # type: ignore
|
||||
actors_metadata = await get_actors_metadata(
|
||||
db_session, [ap_object] # type: ignore
|
||||
)
|
||||
else:
|
||||
actors_metadata = get_actors_metadata(db, [ap_object.actor]) # type: ignore
|
||||
actors_metadata = await get_actors_metadata(
|
||||
db_session, [ap_object.actor] # type: ignore
|
||||
)
|
||||
print(ap_object)
|
||||
return templates.render_template(
|
||||
db,
|
||||
return await templates.render_template(
|
||||
db_session,
|
||||
request,
|
||||
"lookup.html",
|
||||
{
|
||||
|
@ -97,16 +103,18 @@ def get_lookup(
|
|||
|
||||
|
||||
@router.get("/new")
|
||||
def admin_new(
|
||||
async def admin_new(
|
||||
request: Request,
|
||||
query: str | None = None,
|
||||
in_reply_to: str | None = None,
|
||||
db: Session = Depends(get_db),
|
||||
db_session: AsyncSession = Depends(get_db_session),
|
||||
) -> templates.TemplateResponse:
|
||||
content = ""
|
||||
in_reply_to_object = None
|
||||
if in_reply_to:
|
||||
in_reply_to_object = boxes.get_anybox_object_by_ap_id(db, in_reply_to)
|
||||
in_reply_to_object = await boxes.get_anybox_object_by_ap_id(
|
||||
db_session, in_reply_to
|
||||
)
|
||||
|
||||
# Add mentions to the initial note content
|
||||
if not in_reply_to_object:
|
||||
|
@ -117,8 +125,8 @@ def admin_new(
|
|||
if tag.get("type") == "Mention" and tag["name"] != LOCAL_ACTOR.handle:
|
||||
content += f'{tag["name"]} '
|
||||
|
||||
return templates.render_template(
|
||||
db,
|
||||
return await templates.render_template(
|
||||
db_session,
|
||||
request,
|
||||
"admin_new.html",
|
||||
{
|
||||
|
@ -138,28 +146,30 @@ def admin_new(
|
|||
|
||||
|
||||
@router.get("/bookmarks")
|
||||
def admin_bookmarks(
|
||||
async def admin_bookmarks(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
db_session: AsyncSession = Depends(get_db_session),
|
||||
) -> templates.TemplateResponse:
|
||||
stream = (
|
||||
db.scalars(
|
||||
select(models.InboxObject)
|
||||
.where(
|
||||
models.InboxObject.ap_type.in_(
|
||||
["Note", "Article", "Video", "Announce"]
|
||||
),
|
||||
models.InboxObject.is_hidden_from_stream.is_(False),
|
||||
models.InboxObject.undone_by_inbox_object_id.is_(None),
|
||||
models.InboxObject.is_bookmarked.is_(True),
|
||||
(
|
||||
await db_session.scalars(
|
||||
select(models.InboxObject)
|
||||
.where(
|
||||
models.InboxObject.ap_type.in_(
|
||||
["Note", "Article", "Video", "Announce"]
|
||||
),
|
||||
models.InboxObject.is_hidden_from_stream.is_(False),
|
||||
models.InboxObject.undone_by_inbox_object_id.is_(None),
|
||||
models.InboxObject.is_bookmarked.is_(True),
|
||||
)
|
||||
.order_by(models.InboxObject.ap_published_at.desc())
|
||||
.limit(20)
|
||||
)
|
||||
.order_by(models.InboxObject.ap_published_at.desc())
|
||||
.limit(20)
|
||||
).all()
|
||||
# TODO: joinedload + unique
|
||||
)
|
||||
return templates.render_template(
|
||||
db,
|
||||
return await templates.render_template(
|
||||
db_session,
|
||||
request,
|
||||
"admin_stream.html",
|
||||
{
|
||||
|
@ -169,9 +179,9 @@ def admin_bookmarks(
|
|||
|
||||
|
||||
@router.get("/inbox")
|
||||
def admin_inbox(
|
||||
async def admin_inbox(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
db_session: AsyncSession = Depends(get_db_session),
|
||||
filter_by: str | None = None,
|
||||
cursor: str | None = None,
|
||||
) -> templates.TemplateResponse:
|
||||
|
@ -184,17 +194,22 @@ def admin_inbox(
|
|||
)
|
||||
|
||||
page_size = 20
|
||||
remaining_count = db.scalar(select(func.count(models.InboxObject.id)).where(*where))
|
||||
remaining_count = await db_session.scalar(
|
||||
select(func.count(models.InboxObject.id)).where(*where)
|
||||
)
|
||||
q = select(models.InboxObject).where(*where)
|
||||
|
||||
inbox = (
|
||||
db.scalars(
|
||||
q.options(
|
||||
joinedload(models.InboxObject.relates_to_inbox_object),
|
||||
joinedload(models.InboxObject.relates_to_outbox_object),
|
||||
(
|
||||
await db_session.scalars(
|
||||
q.options(
|
||||
joinedload(models.InboxObject.relates_to_inbox_object),
|
||||
joinedload(models.InboxObject.relates_to_outbox_object),
|
||||
joinedload(models.InboxObject.actor),
|
||||
)
|
||||
.order_by(models.InboxObject.ap_published_at.desc())
|
||||
.limit(20)
|
||||
)
|
||||
.order_by(models.InboxObject.ap_published_at.desc())
|
||||
.limit(20)
|
||||
)
|
||||
.unique()
|
||||
.all()
|
||||
|
@ -206,8 +221,8 @@ def admin_inbox(
|
|||
else None
|
||||
)
|
||||
|
||||
actors_metadata = get_actors_metadata(
|
||||
db,
|
||||
actors_metadata = await get_actors_metadata(
|
||||
db_session,
|
||||
[
|
||||
inbox_object.actor
|
||||
for inbox_object in inbox
|
||||
|
@ -215,8 +230,8 @@ def admin_inbox(
|
|||
],
|
||||
)
|
||||
|
||||
return templates.render_template(
|
||||
db,
|
||||
return await templates.render_template(
|
||||
db_session,
|
||||
request,
|
||||
"admin_inbox.html",
|
||||
{
|
||||
|
@ -228,9 +243,9 @@ def admin_inbox(
|
|||
|
||||
|
||||
@router.get("/outbox")
|
||||
def admin_outbox(
|
||||
async def admin_outbox(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
db_session: AsyncSession = Depends(get_db_session),
|
||||
filter_by: str | None = None,
|
||||
cursor: str | None = None,
|
||||
) -> templates.TemplateResponse:
|
||||
|
@ -243,20 +258,22 @@ def admin_outbox(
|
|||
)
|
||||
|
||||
page_size = 20
|
||||
remaining_count = db.scalar(
|
||||
remaining_count = await db_session.scalar(
|
||||
select(func.count(models.OutboxObject.id)).where(*where)
|
||||
)
|
||||
q = select(models.OutboxObject).where(*where)
|
||||
|
||||
outbox = (
|
||||
db.scalars(
|
||||
q.options(
|
||||
joinedload(models.OutboxObject.relates_to_inbox_object),
|
||||
joinedload(models.OutboxObject.relates_to_outbox_object),
|
||||
joinedload(models.OutboxObject.relates_to_actor),
|
||||
(
|
||||
await db_session.scalars(
|
||||
q.options(
|
||||
joinedload(models.OutboxObject.relates_to_inbox_object),
|
||||
joinedload(models.OutboxObject.relates_to_outbox_object),
|
||||
joinedload(models.OutboxObject.relates_to_actor),
|
||||
)
|
||||
.order_by(models.OutboxObject.ap_published_at.desc())
|
||||
.limit(page_size)
|
||||
)
|
||||
.order_by(models.OutboxObject.ap_published_at.desc())
|
||||
.limit(page_size)
|
||||
)
|
||||
.unique()
|
||||
.all()
|
||||
|
@ -268,8 +285,8 @@ def admin_outbox(
|
|||
else None
|
||||
)
|
||||
|
||||
actors_metadata = get_actors_metadata(
|
||||
db,
|
||||
actors_metadata = await get_actors_metadata(
|
||||
db_session,
|
||||
[
|
||||
outbox_object.relates_to_actor
|
||||
for outbox_object in outbox
|
||||
|
@ -277,8 +294,8 @@ def admin_outbox(
|
|||
],
|
||||
)
|
||||
|
||||
return templates.render_template(
|
||||
db,
|
||||
return await templates.render_template(
|
||||
db_session,
|
||||
request,
|
||||
"admin_outbox.html",
|
||||
{
|
||||
|
@ -290,32 +307,34 @@ def admin_outbox(
|
|||
|
||||
|
||||
@router.get("/notifications")
|
||||
def get_notifications(
|
||||
request: Request, db: Session = Depends(get_db)
|
||||
async def get_notifications(
|
||||
request: Request, db_session: AsyncSession = Depends(get_db_session)
|
||||
) -> templates.TemplateResponse:
|
||||
notifications = (
|
||||
db.scalars(
|
||||
select(models.Notification)
|
||||
.options(
|
||||
joinedload(models.Notification.actor),
|
||||
joinedload(models.Notification.inbox_object),
|
||||
joinedload(models.Notification.outbox_object),
|
||||
(
|
||||
await db_session.scalars(
|
||||
select(models.Notification)
|
||||
.options(
|
||||
joinedload(models.Notification.actor),
|
||||
joinedload(models.Notification.inbox_object),
|
||||
joinedload(models.Notification.outbox_object),
|
||||
)
|
||||
.order_by(models.Notification.created_at.desc())
|
||||
)
|
||||
.order_by(models.Notification.created_at.desc())
|
||||
)
|
||||
.unique()
|
||||
.all()
|
||||
)
|
||||
actors_metadata = get_actors_metadata(
|
||||
db, [notif.actor for notif in notifications if notif.actor]
|
||||
actors_metadata = await get_actors_metadata(
|
||||
db_session, [notif.actor for notif in notifications if notif.actor]
|
||||
)
|
||||
|
||||
for notif in notifications:
|
||||
notif.is_new = False
|
||||
db.commit()
|
||||
await db_session.commit()
|
||||
|
||||
return templates.render_template(
|
||||
db,
|
||||
return await templates.render_template(
|
||||
db_session,
|
||||
request,
|
||||
"notifications.html",
|
||||
{
|
||||
|
@ -326,19 +345,19 @@ def get_notifications(
|
|||
|
||||
|
||||
@router.get("/object")
|
||||
def admin_object(
|
||||
async def admin_object(
|
||||
request: Request,
|
||||
ap_id: str,
|
||||
db: Session = Depends(get_db),
|
||||
db_session: AsyncSession = Depends(get_db_session),
|
||||
) -> templates.TemplateResponse:
|
||||
requested_object = boxes.get_anybox_object_by_ap_id(db, ap_id)
|
||||
requested_object = await boxes.get_anybox_object_by_ap_id(db_session, ap_id)
|
||||
if not requested_object:
|
||||
raise HTTPException(status_code=404)
|
||||
|
||||
replies_tree = boxes.get_replies_tree(db, requested_object)
|
||||
replies_tree = await boxes.get_replies_tree(db_session, requested_object)
|
||||
|
||||
return templates.render_template(
|
||||
db,
|
||||
return await templates.render_template(
|
||||
db_session,
|
||||
request,
|
||||
"object.html",
|
||||
{"replies_tree": replies_tree},
|
||||
|
@ -346,30 +365,34 @@ def admin_object(
|
|||
|
||||
|
||||
@router.get("/profile")
|
||||
def admin_profile(
|
||||
async def admin_profile(
|
||||
request: Request,
|
||||
actor_id: str,
|
||||
db: Session = Depends(get_db),
|
||||
db_session: AsyncSession = Depends(get_db_session),
|
||||
) -> templates.TemplateResponse:
|
||||
actor = db.execute(
|
||||
select(models.Actor).where(models.Actor.ap_id == actor_id)
|
||||
actor = (
|
||||
await db_session.execute(
|
||||
select(models.Actor).where(models.Actor.ap_id == actor_id)
|
||||
)
|
||||
).scalar_one_or_none()
|
||||
if not actor:
|
||||
raise HTTPException(status_code=404)
|
||||
|
||||
actors_metadata = get_actors_metadata(db, [actor])
|
||||
actors_metadata = await get_actors_metadata(db_session, [actor])
|
||||
|
||||
inbox_objects = db.scalars(
|
||||
select(models.InboxObject)
|
||||
.where(
|
||||
models.InboxObject.actor_id == actor.id,
|
||||
models.InboxObject.ap_type.in_(["Note", "Article", "Video"]),
|
||||
inbox_objects = (
|
||||
await db_session.scalars(
|
||||
select(models.InboxObject)
|
||||
.where(
|
||||
models.InboxObject.actor_id == actor.id,
|
||||
models.InboxObject.ap_type.in_(["Note", "Article", "Video"]),
|
||||
)
|
||||
.order_by(models.InboxObject.ap_published_at.desc())
|
||||
)
|
||||
.order_by(models.InboxObject.ap_published_at.desc())
|
||||
).all()
|
||||
|
||||
return templates.render_template(
|
||||
db,
|
||||
return await templates.render_template(
|
||||
db_session,
|
||||
request,
|
||||
"admin_profile.html",
|
||||
{
|
||||
|
@ -381,120 +404,120 @@ def admin_profile(
|
|||
|
||||
|
||||
@router.post("/actions/follow")
|
||||
def admin_actions_follow(
|
||||
async def admin_actions_follow(
|
||||
request: Request,
|
||||
ap_actor_id: str = Form(),
|
||||
redirect_url: str = Form(),
|
||||
csrf_check: None = Depends(verify_csrf_token),
|
||||
db: Session = Depends(get_db),
|
||||
db_session: AsyncSession = Depends(get_db_session),
|
||||
) -> RedirectResponse:
|
||||
print(f"Following {ap_actor_id}")
|
||||
send_follow(db, ap_actor_id)
|
||||
await send_follow(db_session, ap_actor_id)
|
||||
return RedirectResponse(redirect_url, status_code=302)
|
||||
|
||||
|
||||
@router.post("/actions/like")
|
||||
def admin_actions_like(
|
||||
async def admin_actions_like(
|
||||
request: Request,
|
||||
ap_object_id: str = Form(),
|
||||
redirect_url: str = Form(),
|
||||
csrf_check: None = Depends(verify_csrf_token),
|
||||
db: Session = Depends(get_db),
|
||||
db_session: AsyncSession = Depends(get_db_session),
|
||||
) -> RedirectResponse:
|
||||
boxes.send_like(db, ap_object_id)
|
||||
await boxes.send_like(db_session, ap_object_id)
|
||||
return RedirectResponse(redirect_url, status_code=302)
|
||||
|
||||
|
||||
@router.post("/actions/undo")
|
||||
def admin_actions_undo(
|
||||
async def admin_actions_undo(
|
||||
request: Request,
|
||||
ap_object_id: str = Form(),
|
||||
redirect_url: str = Form(),
|
||||
csrf_check: None = Depends(verify_csrf_token),
|
||||
db: Session = Depends(get_db),
|
||||
db_session: AsyncSession = Depends(get_db_session),
|
||||
) -> RedirectResponse:
|
||||
boxes.send_undo(db, ap_object_id)
|
||||
await boxes.send_undo(db_session, ap_object_id)
|
||||
return RedirectResponse(redirect_url, status_code=302)
|
||||
|
||||
|
||||
@router.post("/actions/announce")
|
||||
def admin_actions_announce(
|
||||
async def admin_actions_announce(
|
||||
request: Request,
|
||||
ap_object_id: str = Form(),
|
||||
redirect_url: str = Form(),
|
||||
csrf_check: None = Depends(verify_csrf_token),
|
||||
db: Session = Depends(get_db),
|
||||
db_session: AsyncSession = Depends(get_db_session),
|
||||
) -> RedirectResponse:
|
||||
boxes.send_announce(db, ap_object_id)
|
||||
await boxes.send_announce(db_session, ap_object_id)
|
||||
return RedirectResponse(redirect_url, status_code=302)
|
||||
|
||||
|
||||
@router.post("/actions/bookmark")
|
||||
def admin_actions_bookmark(
|
||||
async def admin_actions_bookmark(
|
||||
request: Request,
|
||||
ap_object_id: str = Form(),
|
||||
redirect_url: str = Form(),
|
||||
csrf_check: None = Depends(verify_csrf_token),
|
||||
db: Session = Depends(get_db),
|
||||
db_session: AsyncSession = Depends(get_db_session),
|
||||
) -> RedirectResponse:
|
||||
inbox_object = get_inbox_object_by_ap_id(db, ap_object_id)
|
||||
inbox_object = await get_inbox_object_by_ap_id(db_session, ap_object_id)
|
||||
if not inbox_object:
|
||||
raise ValueError("Should never happen")
|
||||
inbox_object.is_bookmarked = True
|
||||
db.commit()
|
||||
await db_session.commit()
|
||||
return RedirectResponse(redirect_url, status_code=302)
|
||||
|
||||
|
||||
@router.post("/actions/unbookmark")
|
||||
def admin_actions_unbookmark(
|
||||
async def admin_actions_unbookmark(
|
||||
request: Request,
|
||||
ap_object_id: str = Form(),
|
||||
redirect_url: str = Form(),
|
||||
csrf_check: None = Depends(verify_csrf_token),
|
||||
db: Session = Depends(get_db),
|
||||
db_session: AsyncSession = Depends(get_db_session),
|
||||
) -> RedirectResponse:
|
||||
inbox_object = get_inbox_object_by_ap_id(db, ap_object_id)
|
||||
inbox_object = await get_inbox_object_by_ap_id(db_session, ap_object_id)
|
||||
if not inbox_object:
|
||||
raise ValueError("Should never happen")
|
||||
inbox_object.is_bookmarked = False
|
||||
db.commit()
|
||||
await db_session.commit()
|
||||
return RedirectResponse(redirect_url, status_code=302)
|
||||
|
||||
|
||||
@router.post("/actions/pin")
|
||||
def admin_actions_pin(
|
||||
async def admin_actions_pin(
|
||||
request: Request,
|
||||
ap_object_id: str = Form(),
|
||||
redirect_url: str = Form(),
|
||||
csrf_check: None = Depends(verify_csrf_token),
|
||||
db: Session = Depends(get_db),
|
||||
db_session: AsyncSession = Depends(get_db_session),
|
||||
) -> RedirectResponse:
|
||||
outbox_object = get_outbox_object_by_ap_id(db, ap_object_id)
|
||||
outbox_object = await get_outbox_object_by_ap_id(db_session, ap_object_id)
|
||||
if not outbox_object:
|
||||
raise ValueError("Should never happen")
|
||||
outbox_object.is_pinned = True
|
||||
db.commit()
|
||||
await db_session.commit()
|
||||
return RedirectResponse(redirect_url, status_code=302)
|
||||
|
||||
|
||||
@router.post("/actions/unpin")
|
||||
def admin_actions_unpin(
|
||||
async def admin_actions_unpin(
|
||||
request: Request,
|
||||
ap_object_id: str = Form(),
|
||||
redirect_url: str = Form(),
|
||||
csrf_check: None = Depends(verify_csrf_token),
|
||||
db: Session = Depends(get_db),
|
||||
db_session: AsyncSession = Depends(get_db_session),
|
||||
) -> RedirectResponse:
|
||||
outbox_object = get_outbox_object_by_ap_id(db, ap_object_id)
|
||||
outbox_object = await get_outbox_object_by_ap_id(db_session, ap_object_id)
|
||||
if not outbox_object:
|
||||
raise ValueError("Should never happen")
|
||||
outbox_object.is_pinned = False
|
||||
db.commit()
|
||||
await db_session.commit()
|
||||
return RedirectResponse(redirect_url, status_code=302)
|
||||
|
||||
|
||||
@router.post("/actions/new")
|
||||
def admin_actions_new(
|
||||
async def admin_actions_new(
|
||||
request: Request,
|
||||
files: list[UploadFile] = [],
|
||||
content: str = Form(),
|
||||
|
@ -504,16 +527,16 @@ def admin_actions_new(
|
|||
is_sensitive: bool = Form(False),
|
||||
visibility: str = Form(),
|
||||
csrf_check: None = Depends(verify_csrf_token),
|
||||
db: Session = Depends(get_db),
|
||||
db_session: AsyncSession = Depends(get_db_session),
|
||||
) -> RedirectResponse:
|
||||
# XXX: for some reason, no files restuls in an empty single file
|
||||
uploads = []
|
||||
if len(files) >= 1 and files[0].filename:
|
||||
for f in files:
|
||||
upload = save_upload(db, f)
|
||||
upload = await save_upload(db_session, f)
|
||||
uploads.append((upload, f.filename))
|
||||
public_id = boxes.send_create(
|
||||
db,
|
||||
public_id = await boxes.send_create(
|
||||
db_session,
|
||||
source=content,
|
||||
uploads=uploads,
|
||||
in_reply_to=in_reply_to or None,
|
||||
|
@ -528,12 +551,12 @@ def admin_actions_new(
|
|||
|
||||
|
||||
@unauthenticated_router.get("/login")
|
||||
def login(
|
||||
async def login(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
db_session: AsyncSession = Depends(get_db_session),
|
||||
) -> templates.TemplateResponse:
|
||||
return templates.render_template(
|
||||
db,
|
||||
return await templates.render_template(
|
||||
db_session,
|
||||
request,
|
||||
"login.html",
|
||||
{"csrf_token": generate_csrf_token()},
|
||||
|
@ -541,7 +564,7 @@ def login(
|
|||
|
||||
|
||||
@unauthenticated_router.post("/login")
|
||||
def login_validation(
|
||||
async def login_validation(
|
||||
request: Request,
|
||||
password: str = Form(),
|
||||
csrf_check: None = Depends(verify_csrf_token),
|
||||
|
@ -556,7 +579,7 @@ def login_validation(
|
|||
|
||||
|
||||
@router.get("/logout")
|
||||
def logout(
|
||||
async def logout(
|
||||
request: Request,
|
||||
) -> RedirectResponse:
|
||||
resp = RedirectResponse(request.url_for("index"), status_code=302)
|
||||
|
|
296
app/boxes.py
296
app/boxes.py
|
@ -12,7 +12,6 @@ from sqlalchemy import func
|
|||
from sqlalchemy import select
|
||||
from sqlalchemy import update
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy.orm import joinedload
|
||||
|
||||
from app import activitypub as ap
|
||||
|
@ -26,6 +25,7 @@ from app.actor import save_actor
|
|||
from app.ap_object import RemoteObject
|
||||
from app.config import BASE_URL
|
||||
from app.config import ID
|
||||
from app.database import AsyncSession
|
||||
from app.database import now
|
||||
from app.outgoing_activities import new_outgoing_activity
|
||||
from app.source import markdownify
|
||||
|
@ -42,8 +42,8 @@ def outbox_object_id(outbox_id) -> str:
|
|||
return f"{BASE_URL}/o/{outbox_id}"
|
||||
|
||||
|
||||
def save_outbox_object(
|
||||
db: Session,
|
||||
async def save_outbox_object(
|
||||
db_session: AsyncSession,
|
||||
public_id: str,
|
||||
raw_object: ap.RawObject,
|
||||
relates_to_inbox_object_id: int | None = None,
|
||||
|
@ -68,15 +68,15 @@ def save_outbox_object(
|
|||
is_hidden_from_homepage=True if ra.in_reply_to else False,
|
||||
source=source,
|
||||
)
|
||||
db.add(outbox_object)
|
||||
db.commit()
|
||||
db.refresh(outbox_object)
|
||||
db_session.add(outbox_object)
|
||||
await db_session.commit()
|
||||
await db_session.refresh(outbox_object)
|
||||
|
||||
return outbox_object
|
||||
|
||||
|
||||
def send_like(db: Session, ap_object_id: str) -> None:
|
||||
inbox_object = get_inbox_object_by_ap_id(db, ap_object_id)
|
||||
async def send_like(db_session: AsyncSession, ap_object_id: str) -> None:
|
||||
inbox_object = await get_inbox_object_by_ap_id(db_session, ap_object_id)
|
||||
if not inbox_object:
|
||||
raise ValueError(f"{ap_object_id} not found in the inbox")
|
||||
|
||||
|
@ -88,20 +88,22 @@ def send_like(db: Session, ap_object_id: str) -> None:
|
|||
"actor": ID,
|
||||
"object": ap_object_id,
|
||||
}
|
||||
outbox_object = save_outbox_object(
|
||||
db, like_id, like, relates_to_inbox_object_id=inbox_object.id
|
||||
outbox_object = await save_outbox_object(
|
||||
db_session, like_id, like, relates_to_inbox_object_id=inbox_object.id
|
||||
)
|
||||
if not outbox_object.id:
|
||||
raise ValueError("Should never happen")
|
||||
|
||||
inbox_object.liked_via_outbox_object_ap_id = outbox_object.ap_id
|
||||
db.commit()
|
||||
await db_session.commit()
|
||||
|
||||
new_outgoing_activity(db, inbox_object.actor.inbox_url, outbox_object.id)
|
||||
await new_outgoing_activity(
|
||||
db_session, inbox_object.actor.inbox_url, outbox_object.id
|
||||
)
|
||||
|
||||
|
||||
def send_announce(db: Session, ap_object_id: str) -> None:
|
||||
inbox_object = get_inbox_object_by_ap_id(db, ap_object_id)
|
||||
async def send_announce(db_session: AsyncSession, ap_object_id: str) -> None:
|
||||
inbox_object = await get_inbox_object_by_ap_id(db_session, ap_object_id)
|
||||
if not inbox_object:
|
||||
raise ValueError(f"{ap_object_id} not found in the inbox")
|
||||
|
||||
|
@ -118,22 +120,22 @@ def send_announce(db: Session, ap_object_id: str) -> None:
|
|||
inbox_object.ap_actor_id,
|
||||
],
|
||||
}
|
||||
outbox_object = save_outbox_object(
|
||||
db, announce_id, announce, relates_to_inbox_object_id=inbox_object.id
|
||||
outbox_object = await save_outbox_object(
|
||||
db_session, announce_id, announce, relates_to_inbox_object_id=inbox_object.id
|
||||
)
|
||||
if not outbox_object.id:
|
||||
raise ValueError("Should never happen")
|
||||
|
||||
inbox_object.announced_via_outbox_object_ap_id = outbox_object.ap_id
|
||||
db.commit()
|
||||
await db_session.commit()
|
||||
|
||||
recipients = _compute_recipients(db, announce)
|
||||
recipients = await _compute_recipients(db_session, announce)
|
||||
for rcp in recipients:
|
||||
new_outgoing_activity(db, rcp, outbox_object.id)
|
||||
await new_outgoing_activity(db_session, rcp, outbox_object.id)
|
||||
|
||||
|
||||
def send_follow(db: Session, ap_actor_id: str) -> None:
|
||||
actor = fetch_actor(db, ap_actor_id)
|
||||
async def send_follow(db_session: AsyncSession, ap_actor_id: str) -> None:
|
||||
actor = await fetch_actor(db_session, ap_actor_id)
|
||||
|
||||
follow_id = allocate_outbox_id()
|
||||
follow = {
|
||||
|
@ -144,17 +146,17 @@ def send_follow(db: Session, ap_actor_id: str) -> None:
|
|||
"object": ap_actor_id,
|
||||
}
|
||||
|
||||
outbox_object = save_outbox_object(
|
||||
db, follow_id, follow, relates_to_actor_id=actor.id
|
||||
outbox_object = await save_outbox_object(
|
||||
db_session, follow_id, follow, relates_to_actor_id=actor.id
|
||||
)
|
||||
if not outbox_object.id:
|
||||
raise ValueError("Should never happen")
|
||||
|
||||
new_outgoing_activity(db, actor.inbox_url, outbox_object.id)
|
||||
await new_outgoing_activity(db_session, actor.inbox_url, outbox_object.id)
|
||||
|
||||
|
||||
def send_undo(db: Session, ap_object_id: str) -> None:
|
||||
outbox_object_to_undo = get_outbox_object_by_ap_id(db, ap_object_id)
|
||||
async def send_undo(db_session: AsyncSession, ap_object_id: str) -> None:
|
||||
outbox_object_to_undo = await get_outbox_object_by_ap_id(db_session, ap_object_id)
|
||||
if not outbox_object_to_undo:
|
||||
raise ValueError(f"{ap_object_id} not found in the outbox")
|
||||
|
||||
|
@ -172,8 +174,8 @@ def send_undo(db: Session, ap_object_id: str) -> None:
|
|||
"object": ap.remove_context(outbox_object_to_undo.ap_object),
|
||||
}
|
||||
|
||||
outbox_object = save_outbox_object(
|
||||
db,
|
||||
outbox_object = await save_outbox_object(
|
||||
db_session,
|
||||
undo_id,
|
||||
undo,
|
||||
relates_to_outbox_object_id=outbox_object_to_undo.id,
|
||||
|
@ -186,31 +188,33 @@ def send_undo(db: Session, ap_object_id: str) -> None:
|
|||
if outbox_object_to_undo.ap_type == "Follow":
|
||||
if not outbox_object_to_undo.activity_object_ap_id:
|
||||
raise ValueError("Should never happen")
|
||||
followed_actor = fetch_actor(db, outbox_object_to_undo.activity_object_ap_id)
|
||||
new_outgoing_activity(
|
||||
db,
|
||||
followed_actor = await fetch_actor(
|
||||
db_session, outbox_object_to_undo.activity_object_ap_id
|
||||
)
|
||||
await new_outgoing_activity(
|
||||
db_session,
|
||||
followed_actor.inbox_url,
|
||||
outbox_object.id,
|
||||
)
|
||||
# Also remove the follow from the following collection
|
||||
db.execute(
|
||||
await db_session.execute(
|
||||
delete(models.Following).where(
|
||||
models.Following.ap_actor_id == followed_actor.ap_id
|
||||
)
|
||||
)
|
||||
db.commit()
|
||||
await db_session.commit()
|
||||
elif outbox_object_to_undo.ap_type == "Like":
|
||||
liked_object_ap_id = outbox_object_to_undo.activity_object_ap_id
|
||||
if not liked_object_ap_id:
|
||||
raise ValueError("Should never happen")
|
||||
liked_object = get_inbox_object_by_ap_id(db, liked_object_ap_id)
|
||||
liked_object = await get_inbox_object_by_ap_id(db_session, liked_object_ap_id)
|
||||
if not liked_object:
|
||||
raise ValueError(f"Cannot find liked object {liked_object_ap_id}")
|
||||
liked_object.liked_via_outbox_object_ap_id = None
|
||||
|
||||
# Send the Undo to the liked object's actor
|
||||
new_outgoing_activity(
|
||||
db,
|
||||
await new_outgoing_activity(
|
||||
db_session,
|
||||
liked_object.actor.inbox_url, # type: ignore
|
||||
outbox_object.id,
|
||||
)
|
||||
|
@ -218,21 +222,23 @@ def send_undo(db: Session, ap_object_id: str) -> None:
|
|||
announced_object_ap_id = outbox_object_to_undo.activity_object_ap_id
|
||||
if not announced_object_ap_id:
|
||||
raise ValueError("Should never happen")
|
||||
announced_object = get_inbox_object_by_ap_id(db, announced_object_ap_id)
|
||||
announced_object = await get_inbox_object_by_ap_id(
|
||||
db_session, announced_object_ap_id
|
||||
)
|
||||
if not announced_object:
|
||||
raise ValueError(f"Cannot find announced object {announced_object_ap_id}")
|
||||
announced_object.announced_via_outbox_object_ap_id = None
|
||||
|
||||
# Send the Undo to the original recipients
|
||||
recipients = _compute_recipients(db, outbox_object.ap_object)
|
||||
recipients = await _compute_recipients(db_session, outbox_object.ap_object)
|
||||
for rcp in recipients:
|
||||
new_outgoing_activity(db, rcp, outbox_object.id)
|
||||
await new_outgoing_activity(db_session, rcp, outbox_object.id)
|
||||
else:
|
||||
raise ValueError("Should never happen")
|
||||
|
||||
|
||||
def send_create(
|
||||
db: Session,
|
||||
async def send_create(
|
||||
db_session: AsyncSession,
|
||||
source: str,
|
||||
uploads: list[tuple[models.Upload, str]],
|
||||
in_reply_to: str | None,
|
||||
|
@ -243,11 +249,11 @@ def send_create(
|
|||
note_id = allocate_outbox_id()
|
||||
published = now().replace(microsecond=0).isoformat().replace("+00:00", "Z")
|
||||
context = f"{ID}/contexts/" + uuid.uuid4().hex
|
||||
content, tags, mentioned_actors = markdownify(db, source)
|
||||
content, tags, mentioned_actors = await markdownify(db_session, source)
|
||||
attachments = []
|
||||
|
||||
if in_reply_to:
|
||||
in_reply_to_object = get_anybox_object_by_ap_id(db, in_reply_to)
|
||||
in_reply_to_object = await get_anybox_object_by_ap_id(db_session, in_reply_to)
|
||||
if not in_reply_to_object:
|
||||
raise ValueError(f"Invalid in reply to {in_reply_to=}")
|
||||
if not in_reply_to_object.ap_context:
|
||||
|
@ -255,7 +261,7 @@ def send_create(
|
|||
context = in_reply_to_object.ap_context
|
||||
|
||||
if in_reply_to_object.is_from_outbox:
|
||||
db.execute(
|
||||
await db_session.execute(
|
||||
update(models.OutboxObject)
|
||||
.where(
|
||||
models.OutboxObject.ap_id == in_reply_to,
|
||||
|
@ -302,7 +308,7 @@ def send_create(
|
|||
"sensitive": is_sensitive,
|
||||
"attachment": attachments,
|
||||
}
|
||||
outbox_object = save_outbox_object(db, note_id, note, source=source)
|
||||
outbox_object = await save_outbox_object(db_session, note_id, note, source=source)
|
||||
if not outbox_object.id:
|
||||
raise ValueError("Should never happen")
|
||||
|
||||
|
@ -312,24 +318,26 @@ def send_create(
|
|||
tag=tag["name"][1:],
|
||||
outbox_object_id=outbox_object.id,
|
||||
)
|
||||
db.add(tagged_object)
|
||||
db_session.add(tagged_object)
|
||||
|
||||
for (upload, filename) in uploads:
|
||||
outbox_object_attachment = models.OutboxObjectAttachment(
|
||||
filename=filename, outbox_object_id=outbox_object.id, upload_id=upload.id
|
||||
)
|
||||
db.add(outbox_object_attachment)
|
||||
db_session.add(outbox_object_attachment)
|
||||
|
||||
db.commit()
|
||||
await db_session.commit()
|
||||
|
||||
recipients = _compute_recipients(db, note)
|
||||
recipients = await _compute_recipients(db_session, note)
|
||||
for rcp in recipients:
|
||||
new_outgoing_activity(db, rcp, outbox_object.id)
|
||||
await new_outgoing_activity(db_session, rcp, outbox_object.id)
|
||||
|
||||
return note_id
|
||||
|
||||
|
||||
def _compute_recipients(db: Session, ap_object: ap.RawObject) -> set[str]:
|
||||
async def _compute_recipients(
|
||||
db_session: AsyncSession, ap_object: ap.RawObject
|
||||
) -> set[str]:
|
||||
_recipients = []
|
||||
for field in ["to", "cc", "bto", "bcc"]:
|
||||
if field in ap_object:
|
||||
|
@ -343,15 +351,17 @@ def _compute_recipients(db: Session, ap_object: ap.RawObject) -> set[str]:
|
|||
|
||||
# If we got a local collection, assume it's a collection of actors
|
||||
if r.startswith(BASE_URL):
|
||||
for actor in fetch_actor_collection(db, r):
|
||||
for actor in await fetch_actor_collection(db_session, r):
|
||||
recipients.add(actor.shared_inbox_url or actor.inbox_url)
|
||||
|
||||
continue
|
||||
|
||||
# Is it a known actor?
|
||||
known_actor = db.execute(
|
||||
select(models.Actor).where(models.Actor.ap_id == r)
|
||||
).scalar_one_or_none()
|
||||
known_actor = (
|
||||
await db_session.execute(
|
||||
select(models.Actor).where(models.Actor.ap_id == r)
|
||||
)
|
||||
).scalar_one_or_none() # type: ignore
|
||||
if known_actor:
|
||||
recipients.add(known_actor.shared_inbox_url or known_actor.inbox_url)
|
||||
continue
|
||||
|
@ -359,7 +369,7 @@ def _compute_recipients(db: Session, ap_object: ap.RawObject) -> set[str]:
|
|||
# Fetch the object
|
||||
raw_object = ap.fetch(r)
|
||||
if raw_object.get("type") in ap.ACTOR_TYPES:
|
||||
saved_actor = save_actor(db, raw_object)
|
||||
saved_actor = await save_actor(db_session, raw_object)
|
||||
recipients.add(saved_actor.shared_inbox_url or saved_actor.inbox_url)
|
||||
else:
|
||||
# Assume it's a collection of actors
|
||||
|
@ -370,27 +380,43 @@ def _compute_recipients(db: Session, ap_object: ap.RawObject) -> set[str]:
|
|||
return recipients
|
||||
|
||||
|
||||
def get_inbox_object_by_ap_id(db: Session, ap_id: str) -> models.InboxObject | None:
|
||||
return db.execute(
|
||||
select(models.InboxObject).where(models.InboxObject.ap_id == ap_id)
|
||||
).scalar_one_or_none()
|
||||
async def get_inbox_object_by_ap_id(
|
||||
db_session: AsyncSession, ap_id: str
|
||||
) -> models.InboxObject | None:
|
||||
return (
|
||||
await db_session.execute(
|
||||
select(models.InboxObject)
|
||||
.where(models.InboxObject.ap_id == ap_id)
|
||||
.options(
|
||||
joinedload(models.InboxObject.actor),
|
||||
joinedload(models.InboxObject.relates_to_inbox_object),
|
||||
joinedload(models.InboxObject.relates_to_outbox_object),
|
||||
)
|
||||
)
|
||||
).scalar_one_or_none() # type: ignore
|
||||
|
||||
|
||||
def get_outbox_object_by_ap_id(db: Session, ap_id: str) -> models.OutboxObject | None:
|
||||
return db.execute(
|
||||
select(models.OutboxObject).where(models.OutboxObject.ap_id == ap_id)
|
||||
).scalar_one_or_none()
|
||||
async def get_outbox_object_by_ap_id(
|
||||
db_session: AsyncSession, ap_id: str
|
||||
) -> models.OutboxObject | None:
|
||||
return (
|
||||
await db_session.execute(
|
||||
select(models.OutboxObject).where(models.OutboxObject.ap_id == ap_id)
|
||||
)
|
||||
).scalar_one_or_none() # type: ignore
|
||||
|
||||
|
||||
def get_anybox_object_by_ap_id(db: Session, ap_id: str) -> AnyboxObject | None:
|
||||
async def get_anybox_object_by_ap_id(
|
||||
db_session: AsyncSession, ap_id: str
|
||||
) -> AnyboxObject | None:
|
||||
if ap_id.startswith(BASE_URL):
|
||||
return get_outbox_object_by_ap_id(db, ap_id)
|
||||
return await get_outbox_object_by_ap_id(db_session, ap_id)
|
||||
else:
|
||||
return get_inbox_object_by_ap_id(db, ap_id)
|
||||
return await get_inbox_object_by_ap_id(db_session, ap_id)
|
||||
|
||||
|
||||
def _handle_delete_activity(
|
||||
db: Session,
|
||||
async def _handle_delete_activity(
|
||||
db_session: AsyncSession,
|
||||
from_actor: models.Actor,
|
||||
ap_object_to_delete: models.InboxObject,
|
||||
) -> None:
|
||||
|
@ -404,12 +430,12 @@ def _handle_delete_activity(
|
|||
# TODO(ts): do we need to delete related activities? should we keep
|
||||
# bookmarked objects with a deleted flag?
|
||||
logger.info(f"Deleting {ap_object_to_delete.ap_type}/{ap_object_to_delete.ap_id}")
|
||||
db.delete(ap_object_to_delete)
|
||||
db.flush()
|
||||
await db_session.delete(ap_object_to_delete)
|
||||
await db_session.flush()
|
||||
|
||||
|
||||
def _handle_follow_follow_activity(
|
||||
db: Session,
|
||||
async def _handle_follow_follow_activity(
|
||||
db_session: AsyncSession,
|
||||
from_actor: models.Actor,
|
||||
inbox_object: models.InboxObject,
|
||||
) -> None:
|
||||
|
@ -419,8 +445,8 @@ def _handle_follow_follow_activity(
|
|||
ap_actor_id=from_actor.ap_id,
|
||||
)
|
||||
try:
|
||||
db.add(follower)
|
||||
db.flush()
|
||||
db_session.add(follower)
|
||||
await db_session.flush()
|
||||
except IntegrityError:
|
||||
pass # TODO update the existing followe
|
||||
|
||||
|
@ -433,20 +459,20 @@ def _handle_follow_follow_activity(
|
|||
"actor": ID,
|
||||
"object": inbox_object.ap_id,
|
||||
}
|
||||
outbox_activity = save_outbox_object(db, reply_id, reply)
|
||||
outbox_activity = await save_outbox_object(db_session, reply_id, reply)
|
||||
if not outbox_activity.id:
|
||||
raise ValueError("Should never happen")
|
||||
new_outgoing_activity(db, from_actor.inbox_url, outbox_activity.id)
|
||||
await new_outgoing_activity(db_session, from_actor.inbox_url, outbox_activity.id)
|
||||
|
||||
notif = models.Notification(
|
||||
notification_type=models.NotificationType.NEW_FOLLOWER,
|
||||
actor_id=from_actor.id,
|
||||
)
|
||||
db.add(notif)
|
||||
db_session.add(notif)
|
||||
|
||||
|
||||
def _handle_undo_activity(
|
||||
db: Session,
|
||||
async def _handle_undo_activity(
|
||||
db_session: AsyncSession,
|
||||
from_actor: models.Actor,
|
||||
undo_activity: models.InboxObject,
|
||||
ap_activity_to_undo: models.InboxObject,
|
||||
|
@ -462,7 +488,7 @@ def _handle_undo_activity(
|
|||
|
||||
if ap_activity_to_undo.ap_type == "Follow":
|
||||
logger.info(f"Undo follow from {from_actor.ap_id}")
|
||||
db.execute(
|
||||
await db_session.execute(
|
||||
delete(models.Follower).where(
|
||||
models.Follower.inbox_object_id == ap_activity_to_undo.id
|
||||
)
|
||||
|
@ -471,13 +497,13 @@ def _handle_undo_activity(
|
|||
notification_type=models.NotificationType.UNFOLLOW,
|
||||
actor_id=from_actor.id,
|
||||
)
|
||||
db.add(notif)
|
||||
db_session.add(notif)
|
||||
|
||||
elif ap_activity_to_undo.ap_type == "Like":
|
||||
if not ap_activity_to_undo.activity_object_ap_id:
|
||||
raise ValueError("Like without object")
|
||||
liked_obj = get_outbox_object_by_ap_id(
|
||||
db,
|
||||
liked_obj = await get_outbox_object_by_ap_id(
|
||||
db_session,
|
||||
ap_activity_to_undo.activity_object_ap_id,
|
||||
)
|
||||
if not liked_obj:
|
||||
|
@ -494,7 +520,7 @@ def _handle_undo_activity(
|
|||
outbox_object_id=liked_obj.id,
|
||||
inbox_object_id=ap_activity_to_undo.id,
|
||||
)
|
||||
db.add(notif)
|
||||
db_session.add(notif)
|
||||
|
||||
elif ap_activity_to_undo.ap_type == "Announce":
|
||||
if not ap_activity_to_undo.activity_object_ap_id:
|
||||
|
@ -504,8 +530,8 @@ def _handle_undo_activity(
|
|||
f"Undo for announce {ap_activity_to_undo.ap_id}/{announced_obj_ap_id}"
|
||||
)
|
||||
if announced_obj_ap_id.startswith(BASE_URL):
|
||||
announced_obj_from_outbox = get_outbox_object_by_ap_id(
|
||||
db, announced_obj_ap_id
|
||||
announced_obj_from_outbox = await get_outbox_object_by_ap_id(
|
||||
db_session, announced_obj_ap_id
|
||||
)
|
||||
if announced_obj_from_outbox:
|
||||
logger.info("Found in the oubox")
|
||||
|
@ -518,7 +544,7 @@ def _handle_undo_activity(
|
|||
outbox_object_id=announced_obj_from_outbox.id,
|
||||
inbox_object_id=ap_activity_to_undo.id,
|
||||
)
|
||||
db.add(notif)
|
||||
db_session.add(notif)
|
||||
|
||||
# FIXME(ts): what to do with ap_activity_to_undo? flag? delete?
|
||||
else:
|
||||
|
@ -527,8 +553,8 @@ def _handle_undo_activity(
|
|||
# commit will be perfomed in save_to_inbox
|
||||
|
||||
|
||||
def _handle_create_activity(
|
||||
db: Session,
|
||||
async def _handle_create_activity(
|
||||
db_session: AsyncSession,
|
||||
from_actor: models.Actor,
|
||||
created_object: models.InboxObject,
|
||||
) -> None:
|
||||
|
@ -544,7 +570,7 @@ def _handle_create_activity(
|
|||
return None
|
||||
|
||||
if created_object.in_reply_to and created_object.in_reply_to.startswith(BASE_URL):
|
||||
db.execute(
|
||||
await db_session.execute(
|
||||
update(models.OutboxObject)
|
||||
.where(
|
||||
models.OutboxObject.ap_id == created_object.in_reply_to,
|
||||
|
@ -559,12 +585,12 @@ def _handle_create_activity(
|
|||
actor_id=from_actor.id,
|
||||
inbox_object_id=created_object.id,
|
||||
)
|
||||
db.add(notif)
|
||||
db_session.add(notif)
|
||||
|
||||
|
||||
def save_to_inbox(db: Session, raw_object: ap.RawObject) -> None:
|
||||
async def save_to_inbox(db_session: AsyncSession, raw_object: ap.RawObject) -> None:
|
||||
try:
|
||||
actor = fetch_actor(db, ap.get_id(raw_object["actor"]))
|
||||
actor = await fetch_actor(db_session, ap.get_id(raw_object["actor"]))
|
||||
except httpx.HTTPStatusError:
|
||||
logger.exception("Failed to fetch actor")
|
||||
return
|
||||
|
@ -576,7 +602,7 @@ def save_to_inbox(db: Session, raw_object: ap.RawObject) -> None:
|
|||
ra = RemoteObject(ap.unwrap_activity(raw_object), actor=actor)
|
||||
|
||||
if (
|
||||
db.scalar(
|
||||
await db_session.scalar(
|
||||
select(func.count(models.InboxObject.id)).where(
|
||||
models.InboxObject.ap_id == ra.ap_id
|
||||
)
|
||||
|
@ -590,13 +616,13 @@ def save_to_inbox(db: Session, raw_object: ap.RawObject) -> None:
|
|||
relates_to_outbox_object: models.OutboxObject | None = None
|
||||
if ra.activity_object_ap_id:
|
||||
if ra.activity_object_ap_id.startswith(BASE_URL):
|
||||
relates_to_outbox_object = get_outbox_object_by_ap_id(
|
||||
db,
|
||||
relates_to_outbox_object = await get_outbox_object_by_ap_id(
|
||||
db_session,
|
||||
ra.activity_object_ap_id,
|
||||
)
|
||||
else:
|
||||
relates_to_inbox_object = get_inbox_object_by_ap_id(
|
||||
db,
|
||||
relates_to_inbox_object = await get_inbox_object_by_ap_id(
|
||||
db_session,
|
||||
ra.activity_object_ap_id,
|
||||
)
|
||||
|
||||
|
@ -625,27 +651,29 @@ def save_to_inbox(db: Session, raw_object: ap.RawObject) -> None:
|
|||
), # TODO: handle mentions
|
||||
)
|
||||
|
||||
db.add(inbox_object)
|
||||
db.flush()
|
||||
db.refresh(inbox_object)
|
||||
db_session.add(inbox_object)
|
||||
await db_session.flush()
|
||||
await db_session.refresh(inbox_object)
|
||||
|
||||
if ra.ap_type == "Note": # TODO: handle create better
|
||||
_handle_create_activity(db, actor, inbox_object)
|
||||
await _handle_create_activity(db_session, actor, inbox_object)
|
||||
elif ra.ap_type == "Update":
|
||||
pass
|
||||
elif ra.ap_type == "Delete":
|
||||
if relates_to_inbox_object:
|
||||
_handle_delete_activity(db, actor, relates_to_inbox_object)
|
||||
await _handle_delete_activity(db_session, actor, relates_to_inbox_object)
|
||||
else:
|
||||
# TODO(ts): handle delete actor
|
||||
logger.info(
|
||||
f"Received a Delete for an unknown object: {ra.activity_object_ap_id}"
|
||||
)
|
||||
elif ra.ap_type == "Follow":
|
||||
_handle_follow_follow_activity(db, actor, inbox_object)
|
||||
await _handle_follow_follow_activity(db_session, actor, inbox_object)
|
||||
elif ra.ap_type == "Undo":
|
||||
if relates_to_inbox_object:
|
||||
_handle_undo_activity(db, actor, inbox_object, relates_to_inbox_object)
|
||||
await _handle_undo_activity(
|
||||
db_session, actor, inbox_object, relates_to_inbox_object
|
||||
)
|
||||
else:
|
||||
logger.info("Received Undo for an unknown activity")
|
||||
elif ra.ap_type in ["Accept", "Reject"]:
|
||||
|
@ -661,7 +689,7 @@ def save_to_inbox(db: Session, raw_object: ap.RawObject) -> None:
|
|||
outbox_object_id=relates_to_outbox_object.id,
|
||||
ap_actor_id=actor.ap_id,
|
||||
)
|
||||
db.add(following)
|
||||
db_session.add(following)
|
||||
else:
|
||||
logger.info(
|
||||
"Received an Accept for an unsupported activity: "
|
||||
|
@ -689,7 +717,7 @@ def save_to_inbox(db: Session, raw_object: ap.RawObject) -> None:
|
|||
outbox_object_id=relates_to_outbox_object.id,
|
||||
inbox_object_id=inbox_object.id,
|
||||
)
|
||||
db.add(notif)
|
||||
db_session.add(notif)
|
||||
elif raw_object["type"] == "Announce":
|
||||
if relates_to_outbox_object:
|
||||
# This is an announce for a local object
|
||||
|
@ -703,7 +731,7 @@ def save_to_inbox(db: Session, raw_object: ap.RawObject) -> None:
|
|||
outbox_object_id=relates_to_outbox_object.id,
|
||||
inbox_object_id=inbox_object.id,
|
||||
)
|
||||
db.add(notif)
|
||||
db_session.add(notif)
|
||||
else:
|
||||
# This is announce for a maybe unknown object
|
||||
if relates_to_inbox_object:
|
||||
|
@ -713,7 +741,9 @@ def save_to_inbox(db: Session, raw_object: ap.RawObject) -> None:
|
|||
if not ra.activity_object_ap_id:
|
||||
raise ValueError("Should never happen")
|
||||
announced_raw_object = ap.fetch(ra.activity_object_ap_id)
|
||||
announced_actor = fetch_actor(db, ap.get_actor_id(announced_raw_object))
|
||||
announced_actor = await fetch_actor(
|
||||
db_session, ap.get_actor_id(announced_raw_object)
|
||||
)
|
||||
announced_object = RemoteObject(announced_raw_object, announced_actor)
|
||||
announced_inbox_object = models.InboxObject(
|
||||
server=urlparse(announced_object.ap_id).netloc,
|
||||
|
@ -727,8 +757,8 @@ def save_to_inbox(db: Session, raw_object: ap.RawObject) -> None:
|
|||
visibility=announced_object.visibility,
|
||||
is_hidden_from_stream=True,
|
||||
)
|
||||
db.add(announced_inbox_object)
|
||||
db.flush()
|
||||
db_session.add(announced_inbox_object)
|
||||
await db_session.flush()
|
||||
inbox_object.relates_to_inbox_object_id = announced_inbox_object.id
|
||||
elif ra.ap_type in ["Like", "Announce"]:
|
||||
if not relates_to_outbox_object:
|
||||
|
@ -749,7 +779,7 @@ def save_to_inbox(db: Session, raw_object: ap.RawObject) -> None:
|
|||
outbox_object_id=relates_to_outbox_object.id,
|
||||
inbox_object_id=inbox_object.id,
|
||||
)
|
||||
db.add(notif)
|
||||
db_session.add(notif)
|
||||
elif raw_object["type"] == "Announce":
|
||||
# TODO(ts): notification
|
||||
relates_to_outbox_object.announces_count = (
|
||||
|
@ -762,18 +792,18 @@ def save_to_inbox(db: Session, raw_object: ap.RawObject) -> None:
|
|||
outbox_object_id=relates_to_outbox_object.id,
|
||||
inbox_object_id=inbox_object.id,
|
||||
)
|
||||
db.add(notif)
|
||||
|