- Fix audit event names in CLI prune/clear commands from `cli.seo.*` to `cli.seo_meta.*`
- Refactor `_delete_attachment_file` to accept full attachment dict instead of storage_path string, using directory and stored_name fields with ATTACHMENTS_DIR
- Add `safe_next` validation for referer header in validation error redirect and media redirect
- Add `is_active` check in login router to reject deactivated accounts with "Account is deactivated" error
- Replace raw `request.headers.get("Referer")` with `redirect_back()` utility in bookmarks, polls, reactions, and votes routers
- Move `mark_conversation_read` call from `get_conversation_messages` to `messages_page` to avoid side effects during message retrieval
- Fix poll audit link to use `option.get("label")` instead of `option.get("text")`
- Add `VOTABLE` set validation in votes router to reject invalid target types with 400 response
- Strip control characters (0x00-0x20) from URLs in `_safe_url` instead of simple strip
- Add `__getattr__` fallback in services `__init__.py` for dynamic attribute access
41 lines
1.2 KiB
Python
41 lines
1.2 KiB
Python
# retoor <retoor@molodetz.nl>
|
|
|
|
import logging
|
|
from typing import Annotated
|
|
from fastapi import Depends, APIRouter, Request
|
|
from fastapi.responses import RedirectResponse, JSONResponse
|
|
from devplacepy.utils import require_user, redirect_back
|
|
from devplacepy.content import apply_vote
|
|
from devplacepy.models import VoteForm
|
|
from devplacepy.dependencies import json_or_form
|
|
|
|
logger = logging.getLogger(__name__)
|
|
router = APIRouter()
|
|
|
|
VOTABLE = {"post", "comment", "gist", "project"}
|
|
|
|
|
|
@router.post("/{target_type}/{target_uid}")
|
|
async def vote(
|
|
request: Request,
|
|
target_type: str,
|
|
target_uid: str,
|
|
data: Annotated[VoteForm, Depends(json_or_form(VoteForm))],
|
|
):
|
|
user = require_user(request)
|
|
if target_type not in VOTABLE:
|
|
return JSONResponse({"error": "Invalid target"}, status_code=400)
|
|
result = apply_vote(request, user, target_type, target_uid, data.value)
|
|
|
|
if request.headers.get("x-requested-with") == "fetch":
|
|
logger.debug(
|
|
"ajax vote response target=%s/%s net=%s value=%s",
|
|
target_type,
|
|
target_uid,
|
|
result["net"],
|
|
result["value"],
|
|
)
|
|
return JSONResponse(result)
|
|
|
|
return RedirectResponse(url=redirect_back(request), status_code=302)
|