# retoor <retoor@molodetz.nl>
import logging
from fastapi import APIRouter, Request
from fastapi.responses import FileResponse, JSONResponse
from devplacepy import push
from devplacepy.config import STATIC_DIR
from devplacepy.push import providers
from devplacepy.utils import require_user_api
from urllib.parse import urlparse
from devplacepy.services.audit import record as audit
logger = logging.getLogger(__name__)
router = APIRouter()
WELCOME_PAYLOAD = {
"title": "DevPlace",
"message": "Push notifications enabled.",
"icon": "/static/apple-touch-icon.png",
"url": "/notifications",
}
@router.get("/push.json")
async def push_public_key() -> JSONResponse:
configs = providers.client_config()
webpush = configs.get(providers.DEFAULT_PROVIDER, {})
return JSONResponse(
{"publicKey": webpush.get("publicKey", ""), "providers": configs}
)
@router.post("/push.json")
async def push_register(request: Request) -> JSONResponse:
user = require_user_api(request)
try:
body = await request.json()
except ValueError:
return JSONResponse({"error": "Invalid JSON"}, status_code=400)
if not isinstance(body, dict):
return JSONResponse({"error": "Invalid request"}, status_code=400)
provider = providers.get(body.get("provider"))
if provider is None or not providers.is_active(provider):
return JSONResponse({"error": "Unknown provider"}, status_code=400)
fields = provider.parse_registration(body)
if fields is None:
return JSONResponse({"error": "Invalid request"}, status_code=400)
_, created = push.register(user["uid"], provider.name, fields)
if created:
try:
await push.notify_user(user["uid"], WELCOME_PAYLOAD)
except Exception as exc:
logger.warning("Welcome push failed for %s: %s", user["uid"], exc)
audit.record(
request,
"push.subscribe" if created else "push.update",
user=user,
target_type="user",
target_uid=user["uid"],
target_label=user.get("username"),
metadata={
"provider": provider.name,
"endpoint_host": urlparse(fields["endpoint"]).hostname
if fields.get("endpoint")
else None,
"created": created,
},
summary=f"{user.get('username')} {'registered' if created else 'updated'} a push subscription",
links=[audit.target("user", user["uid"], user.get("username"))],
)
return JSONResponse({"registered": True})
@router.get("/service-worker.js")
async def service_worker() -> FileResponse:
return FileResponse(
STATIC_DIR / "service-worker.js",
media_type="application/javascript",
headers={"Service-Worker-Allowed": "/", "Cache-Control": "no-cache"},
)
@router.get("/manifest.json")
async def manifest() -> FileResponse:
return FileResponse(
STATIC_DIR / "manifest.json", media_type="application/manifest+json"
)