Bootstrap webmention endpoint
This commit is contained in:
parent
1b3c76ee2f
commit
c6bc53ce54
5 changed files with 104 additions and 29 deletions
62
app/webmentions.py
Normal file
62
app/webmentions.py
Normal file
|
@ -0,0 +1,62 @@
|
|||
from bs4 import BeautifulSoup # type: ignore
|
||||
from fastapi import APIRouter
|
||||
from fastapi import HTTPException
|
||||
from fastapi import Request
|
||||
from fastapi.responses import JSONResponse
|
||||
from loguru import logger
|
||||
|
||||
from app.utils import microformats
|
||||
from app.utils.url import check_url
|
||||
from app.utils.url import is_url_valid
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
def is_source_containing_target(source_html: str, target_url: str) -> bool:
|
||||
soup = BeautifulSoup(source_html, "html5lib")
|
||||
for link in soup.find_all("a"):
|
||||
h = link.get("href")
|
||||
if not is_url_valid(h):
|
||||
continue
|
||||
|
||||
if h == target_url:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
@router.post("/webmentions")
|
||||
async def webmention_endpoint(
|
||||
request: Request,
|
||||
) -> JSONResponse:
|
||||
form_data = await request.form()
|
||||
try:
|
||||
source = form_data["source"]
|
||||
target = form_data["target"]
|
||||
|
||||
if source == target:
|
||||
raise ValueError("source URL is the same as target")
|
||||
|
||||
check_url(source)
|
||||
check_url(target)
|
||||
except Exception:
|
||||
logger.exception("Invalid webmention request")
|
||||
raise HTTPException(status_code=400, detail="Invalid payload")
|
||||
|
||||
logger.info(f"Received webmention {source=} {target=}")
|
||||
|
||||
# TODO: get outbox via ap_id (URL is the same as ap_id)
|
||||
maybe_data_and_html = await microformats.fetch_and_parse(source)
|
||||
if not maybe_data_and_html:
|
||||
logger.info("failed to fetch source")
|
||||
raise HTTPException(status_code=400, detail="failed to fetch source")
|
||||
|
||||
data, html = maybe_data_and_html
|
||||
|
||||
if not is_source_containing_target(html, target):
|
||||
logger.warning("target not found in source")
|
||||
raise HTTPException(status_code=400, detail="target not found in source")
|
||||
|
||||
logger.info(f"{data=}")
|
||||
|
||||
return JSONResponse(content={}, status_code=200)
|
Loading…
Add table
Add a link
Reference in a new issue