Introduce WILDCARD_TOKENS set in attachments.py to treat "*", ".*", "*.*" as wildcards that fall back to ALLOWED_UPLOAD_TYPES. Add cmd_token_issue and cmd_token_list CLI commands for issuing and listing DevPlace access tokens. Register access_tokens table in SOFT_DELETE_TABLES and initialize its columns and indexes in init_db. Replace Form() with Depends(json_or_form(...)) in admin backup, container, notification, settings, and user routers to support both JSON and form data.
83 lines
2.9 KiB
Python
83 lines
2.9 KiB
Python
# retoor <retoor@molodetz.nl>
|
|
|
|
import logging
|
|
from typing import Annotated
|
|
from fastapi import Depends, APIRouter, Request
|
|
from devplacepy.models import NotificationPrefForm
|
|
from devplacepy.database import set_notification_pref, reset_notification_prefs
|
|
from devplacepy.responses import action_result, json_error
|
|
from devplacepy.services.audit import record as audit
|
|
|
|
from devplacepy.routers.profile._shared import resolve_customization_target
|
|
from devplacepy.dependencies import json_or_form
|
|
|
|
logger = logging.getLogger(__name__)
|
|
router = APIRouter()
|
|
|
|
@router.post("/{username}/notifications")
|
|
async def set_notification_preference(
|
|
request: Request,
|
|
username: str,
|
|
data: Annotated[NotificationPrefForm, Depends(json_or_form(NotificationPrefForm))],
|
|
):
|
|
target, denied = resolve_customization_target(request, username)
|
|
if denied is not None:
|
|
return denied
|
|
try:
|
|
set_notification_pref(
|
|
target["uid"], data.notification_type, data.channel, data.value
|
|
)
|
|
except ValueError:
|
|
return json_error(400, "Unknown notification type or channel")
|
|
logger.info(
|
|
f"Notification {data.notification_type}/{data.channel} "
|
|
f"{'enabled' if data.value else 'disabled'} for {target['username']}"
|
|
)
|
|
audit.record(
|
|
request,
|
|
"profile.notification.toggle",
|
|
target_type="user",
|
|
target_uid=target["uid"],
|
|
target_label=target["username"],
|
|
old_value=0 if data.value else 1,
|
|
new_value=1 if data.value else 0,
|
|
summary=(
|
|
f"{'enabled' if data.value else 'disabled'} {data.channel} "
|
|
f"{data.notification_type} notifications for {target['username']}"
|
|
),
|
|
metadata={"type": data.notification_type, "channel": data.channel},
|
|
links=[audit.target("user", target["uid"], target["username"])],
|
|
)
|
|
url = f"/profile/{target['username']}?tab=notifications"
|
|
return action_result(
|
|
request,
|
|
url,
|
|
data={
|
|
"url": url,
|
|
"notification_type": data.notification_type,
|
|
"channel": data.channel,
|
|
"value": data.value,
|
|
},
|
|
)
|
|
|
|
@router.post("/{username}/notifications/reset")
|
|
async def reset_notification_preferences(request: Request, username: str):
|
|
target, denied = resolve_customization_target(request, username)
|
|
if denied is not None:
|
|
return denied
|
|
removed = reset_notification_prefs(target["uid"])
|
|
logger.info(
|
|
f"Notification preferences reset for {target['username']} ({removed} rows)"
|
|
)
|
|
audit.record(
|
|
request,
|
|
"profile.notification.reset",
|
|
target_type="user",
|
|
target_uid=target["uid"],
|
|
target_label=target["username"],
|
|
summary=f"reset notification preferences for {target['username']}",
|
|
links=[audit.target("user", target["uid"], target["username"])],
|
|
)
|
|
url = f"/profile/{target['username']}?tab=notifications"
|
|
return action_result(request, url, data={"url": url})
|