Micropub tweaks

This commit is contained in:
Thomas Sileo 2022-07-17 20:43:03 +02:00
parent 6f25d06bbb
commit 88ca8a676d
3 changed files with 76 additions and 9 deletions

View file

@ -5,6 +5,7 @@ from fastapi import Depends
from fastapi import Request from fastapi import Request
from fastapi.responses import JSONResponse from fastapi.responses import JSONResponse
from fastapi.responses import RedirectResponse from fastapi.responses import RedirectResponse
from loguru import logger
from app import activitypub as ap from app import activitypub as ap
from app.boxes import get_outbox_object_by_ap_id from app.boxes import get_outbox_object_by_ap_id
@ -55,6 +56,14 @@ async def micropub_endpoint(
return {} 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") @router.post("/micropub")
async def post_micropub_endpoint( async def post_micropub_endpoint(
request: Request, request: Request,
@ -62,8 +71,17 @@ async def post_micropub_endpoint(
db_session: AsyncSession = Depends(get_db_session), db_session: AsyncSession = Depends(get_db_session),
) -> RedirectResponse | JSONResponse: ) -> RedirectResponse | JSONResponse:
form_data = await request.form() 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 "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( outbox_object = await get_outbox_object_by_ap_id(
db_session, form_data["url"] db_session, form_data["url"]
) )
@ -75,14 +93,48 @@ async def post_micropub_endpoint(
}, },
status_code=400, status_code=400,
) )
await send_delete(db_session, outbox_object.ap_id) # type: ignore
return JSONResponse(content={}, status_code=200)
h = "entry" if form_data["action"] == "delete":
if "h" in form_data: if "delete" not in access_token_info.scopes:
h = form_data["h"] 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)
if h != "entry": 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}"
logger.info(f"Creating {entry_type}")
if entry_type != "h-entry":
return JSONResponse( return JSONResponse(
content={ content={
"error": "invalid_request", "error": "invalid_request",
@ -91,7 +143,13 @@ async def post_micropub_endpoint(
status_code=400, status_code=400,
) )
content = form_data["content"] # 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( public_id = await send_create(
db_session, db_session,
content, content,

View file

@ -11,7 +11,7 @@
<div style="flex:1;"> <div style="flex:1;">
<div style="margin-top:20px"> <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> <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> </div>
</div> </div>

View file

@ -1,5 +1,6 @@
from dataclasses import dataclass from dataclasses import dataclass
from typing import Any from typing import Any
from urllib.parse import urlparse
from app.utils import microformats from app.utils import microformats
from app.utils.url import make_abs 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: 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) maybe_data_and_html = await microformats.fetch_and_parse(url)
if maybe_data_and_html is not None: if maybe_data_and_html is not None:
data: dict[str, Any] = maybe_data_and_html[0] data: dict[str, Any] = maybe_data_and_html[0]