mirror of
https://git.sr.ht/~tsileo/microblog.pub
synced 2024-12-22 13:14:28 +00:00
Micropub tweaks
This commit is contained in:
parent
6f25d06bbb
commit
88ca8a676d
3 changed files with 76 additions and 9 deletions
|
@ -5,6 +5,7 @@ from fastapi import Depends
|
|||
from fastapi import Request
|
||||
from fastapi.responses import JSONResponse
|
||||
from fastapi.responses import RedirectResponse
|
||||
from loguru import logger
|
||||
|
||||
from app import activitypub as ap
|
||||
from app.boxes import get_outbox_object_by_ap_id
|
||||
|
@ -55,6 +56,14 @@ async def micropub_endpoint(
|
|||
return {}
|
||||
|
||||
|
||||
def _prop_get(dat: dict[str, Any], key: str) -> str:
|
||||
val = dat[key]
|
||||
if isinstance(val, list):
|
||||
return val[0]
|
||||
else:
|
||||
return val
|
||||
|
||||
|
||||
@router.post("/micropub")
|
||||
async def post_micropub_endpoint(
|
||||
request: Request,
|
||||
|
@ -62,8 +71,17 @@ async def post_micropub_endpoint(
|
|||
db_session: AsyncSession = Depends(get_db_session),
|
||||
) -> RedirectResponse | JSONResponse:
|
||||
form_data = await request.form()
|
||||
is_json = False
|
||||
if not form_data:
|
||||
form_data = await request.json()
|
||||
is_json = True
|
||||
|
||||
insufficient_scope_resp = JSONResponse(
|
||||
status_code=401, content={"error": "insufficient_scope"}
|
||||
)
|
||||
|
||||
if "action" in form_data:
|
||||
if form_data["action"] == "delete":
|
||||
if form_data["action"] in ["delete", "update"]:
|
||||
outbox_object = await get_outbox_object_by_ap_id(
|
||||
db_session, form_data["url"]
|
||||
)
|
||||
|
@ -75,14 +93,48 @@ async def post_micropub_endpoint(
|
|||
},
|
||||
status_code=400,
|
||||
)
|
||||
|
||||
if form_data["action"] == "delete":
|
||||
if "delete" not in access_token_info.scopes:
|
||||
return insufficient_scope_resp
|
||||
logger.info(f"Deleting object {outbox_object.ap_id}")
|
||||
await send_delete(db_session, outbox_object.ap_id) # type: ignore
|
||||
return JSONResponse(content={}, status_code=200)
|
||||
|
||||
elif form_data["action"] == "update":
|
||||
if "update" not in access_token_info.scopes:
|
||||
return insufficient_scope_resp
|
||||
|
||||
# TODO(ts): support update
|
||||
# "replace": {"content": ["new content"]}
|
||||
|
||||
logger.info(f"Updating object {outbox_object.ap_id}: {form_data}")
|
||||
return JSONResponse(content={}, status_code=200)
|
||||
else:
|
||||
raise ValueError("Should never happen")
|
||||
else:
|
||||
return JSONResponse(
|
||||
content={
|
||||
"error": "invalid_request",
|
||||
"error_description": f'Unsupported action: {form_data["action"]}',
|
||||
},
|
||||
status_code=400,
|
||||
)
|
||||
|
||||
if "create" not in access_token_info.scopes:
|
||||
return insufficient_scope_resp
|
||||
|
||||
if is_json:
|
||||
entry_type = _prop_get(form_data, "type") # type: ignore
|
||||
else:
|
||||
h = "entry"
|
||||
if "h" in form_data:
|
||||
h = form_data["h"]
|
||||
entry_type = f"h-{h}"
|
||||
|
||||
if h != "entry":
|
||||
logger.info(f"Creating {entry_type}")
|
||||
|
||||
if entry_type != "h-entry":
|
||||
return JSONResponse(
|
||||
content={
|
||||
"error": "invalid_request",
|
||||
|
@ -91,7 +143,13 @@ async def post_micropub_endpoint(
|
|||
status_code=400,
|
||||
)
|
||||
|
||||
# TODO(ts): support creating Article (with a name)
|
||||
|
||||
if is_json:
|
||||
content = _prop_get(form_data["properties"], "content") # type: ignore
|
||||
else:
|
||||
content = form_data["content"]
|
||||
|
||||
public_id = await send_create(
|
||||
db_session,
|
||||
content,
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
<div style="flex:1;">
|
||||
<div style="margin-top:20px">
|
||||
<a class="lcolor" style="font-size:1.2em;font-weight:600;text-decoration:none;" href="{{ client.url }}">{{ client.name }}</a>
|
||||
<p>wants you to login as <strong class="lcolor">{{ me }}</strong></p>
|
||||
<p>wants you to login as <strong class="lcolor">{{ me }}</strong> with the following redirect URI: <code>{{ redirect_uri }}</code>.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from dataclasses import dataclass
|
||||
from typing import Any
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from app.utils import microformats
|
||||
from app.utils.url import make_abs
|
||||
|
@ -22,6 +23,14 @@ def _get_prop(props: dict[str, Any], name: str, default=None) -> Any:
|
|||
|
||||
|
||||
async def get_client_id_data(url: str) -> IndieAuthClient | None:
|
||||
# Don't fetch localhost URL
|
||||
if urlparse(url).netloc == "localhost":
|
||||
return IndieAuthClient(
|
||||
logo=None,
|
||||
name=url,
|
||||
url=url,
|
||||
)
|
||||
|
||||
maybe_data_and_html = await microformats.fetch_and_parse(url)
|
||||
if maybe_data_and_html is not None:
|
||||
data: dict[str, Any] = maybe_data_and_html[0]
|
||||
|
|
Loading…
Reference in a new issue