Backend:
- Optimize file listing for non-superusers with dedicated CRUD methods
- Add get_visible_for_user and count_visible_for_user for efficient queries
- Move /allowed-types/ and /max-size/ routes before /{file_id} for proper matching
- Rename notification 'metadata' field to 'extra_data' for clarity
- Fix settings export to use get_value() method
Frontend:
- Update NotificationItem interface to use extra_data field
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
245 lines
6.5 KiB
Python
245 lines
6.5 KiB
Python
"""Notification API endpoints."""
|
|
|
|
import json
|
|
from typing import Any, Optional
|
|
from fastapi import APIRouter, Depends, HTTPException, status, Query
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.dependencies import get_db, get_current_user, get_current_superuser
|
|
from app.models.user import User
|
|
from app import crud
|
|
from app.schemas.notification import (
|
|
Notification,
|
|
NotificationCreate,
|
|
NotificationCreateForUser,
|
|
NotificationList,
|
|
NotificationStats,
|
|
NotificationBulkAction
|
|
)
|
|
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
def serialize_notification(db_obj) -> dict:
|
|
"""Serialize notification for response."""
|
|
extra_data = None
|
|
if db_obj.extra_data:
|
|
try:
|
|
extra_data = json.loads(db_obj.extra_data)
|
|
except json.JSONDecodeError:
|
|
extra_data = None
|
|
|
|
return {
|
|
"id": db_obj.id,
|
|
"user_id": db_obj.user_id,
|
|
"title": db_obj.title,
|
|
"message": db_obj.message,
|
|
"type": db_obj.type,
|
|
"link": db_obj.link,
|
|
"extra_data": extra_data,
|
|
"is_read": db_obj.is_read,
|
|
"created_at": db_obj.created_at,
|
|
"read_at": db_obj.read_at
|
|
}
|
|
|
|
|
|
@router.get("", response_model=NotificationList)
|
|
def get_notifications(
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(get_current_user),
|
|
skip: int = Query(0, ge=0),
|
|
limit: int = Query(50, ge=1, le=100),
|
|
unread_only: bool = False
|
|
) -> Any:
|
|
"""
|
|
Get notifications for the current user.
|
|
"""
|
|
notifications = crud.notification.get_multi_by_user(
|
|
db,
|
|
user_id=current_user.id,
|
|
skip=skip,
|
|
limit=limit,
|
|
unread_only=unread_only
|
|
)
|
|
total = crud.notification.count_by_user(db, current_user.id)
|
|
unread_count = crud.notification.count_unread_by_user(db, current_user.id)
|
|
|
|
return {
|
|
"items": [serialize_notification(n) for n in notifications],
|
|
"total": total,
|
|
"unread_count": unread_count
|
|
}
|
|
|
|
|
|
@router.get("/unread-count")
|
|
def get_unread_count(
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(get_current_user),
|
|
) -> Any:
|
|
"""
|
|
Get unread notification count for the current user.
|
|
"""
|
|
count = crud.notification.count_unread_by_user(db, current_user.id)
|
|
return {"unread_count": count}
|
|
|
|
|
|
@router.get("/stats", response_model=NotificationStats)
|
|
def get_notification_stats(
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(get_current_user),
|
|
) -> Any:
|
|
"""
|
|
Get notification statistics for the current user.
|
|
"""
|
|
return crud.notification.get_stats_by_user(db, current_user.id)
|
|
|
|
|
|
@router.get("/{notification_id}", response_model=Notification)
|
|
def get_notification(
|
|
notification_id: str,
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(get_current_user),
|
|
) -> Any:
|
|
"""
|
|
Get a specific notification.
|
|
"""
|
|
db_obj = crud.notification.get(db, id=notification_id)
|
|
if not db_obj or db_obj.user_id != current_user.id:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail="Notification not found"
|
|
)
|
|
|
|
return serialize_notification(db_obj)
|
|
|
|
|
|
@router.post("/{notification_id}/read", response_model=Notification)
|
|
def mark_as_read(
|
|
notification_id: str,
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(get_current_user),
|
|
) -> Any:
|
|
"""
|
|
Mark a notification as read.
|
|
"""
|
|
db_obj = crud.notification.mark_as_read(
|
|
db, id=notification_id, user_id=current_user.id
|
|
)
|
|
if not db_obj:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail="Notification not found"
|
|
)
|
|
|
|
return serialize_notification(db_obj)
|
|
|
|
|
|
@router.post("/read-all")
|
|
def mark_all_as_read(
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(get_current_user),
|
|
) -> Any:
|
|
"""
|
|
Mark all notifications as read.
|
|
"""
|
|
count = crud.notification.mark_all_as_read(db, user_id=current_user.id)
|
|
return {"marked_as_read": count}
|
|
|
|
|
|
@router.post("/read-multiple")
|
|
def mark_multiple_as_read(
|
|
action: NotificationBulkAction,
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(get_current_user),
|
|
) -> Any:
|
|
"""
|
|
Mark multiple notifications as read.
|
|
"""
|
|
count = crud.notification.mark_multiple_as_read(
|
|
db,
|
|
user_id=current_user.id,
|
|
notification_ids=action.notification_ids
|
|
)
|
|
return {"marked_as_read": count}
|
|
|
|
|
|
@router.delete("/{notification_id}", status_code=status.HTTP_204_NO_CONTENT)
|
|
def delete_notification(
|
|
notification_id: str,
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(get_current_user),
|
|
) -> None:
|
|
"""
|
|
Delete a notification.
|
|
"""
|
|
if not crud.notification.delete(db, id=notification_id, user_id=current_user.id):
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail="Notification not found"
|
|
)
|
|
|
|
|
|
@router.delete("/read/all")
|
|
def delete_all_read(
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(get_current_user),
|
|
) -> Any:
|
|
"""
|
|
Delete all read notifications.
|
|
"""
|
|
count = crud.notification.delete_all_read(db, user_id=current_user.id)
|
|
return {"deleted": count}
|
|
|
|
|
|
@router.post("/delete-multiple")
|
|
def delete_multiple(
|
|
action: NotificationBulkAction,
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(get_current_user),
|
|
) -> Any:
|
|
"""
|
|
Delete multiple notifications.
|
|
"""
|
|
count = crud.notification.delete_multiple(
|
|
db,
|
|
user_id=current_user.id,
|
|
notification_ids=action.notification_ids
|
|
)
|
|
return {"deleted": count}
|
|
|
|
|
|
# Admin endpoints
|
|
|
|
@router.post("/admin/send", status_code=status.HTTP_201_CREATED)
|
|
def send_notification_to_user(
|
|
notification_in: NotificationCreate,
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(get_current_superuser),
|
|
) -> Any:
|
|
"""
|
|
Send a notification to a specific user (admin only).
|
|
"""
|
|
db_obj = crud.notification.create(db, obj_in=notification_in)
|
|
return serialize_notification(db_obj)
|
|
|
|
|
|
@router.post("/admin/broadcast")
|
|
def broadcast_notification(
|
|
notification_in: NotificationCreateForUser,
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(get_current_superuser),
|
|
) -> Any:
|
|
"""
|
|
Send a notification to all users (admin only).
|
|
"""
|
|
count = crud.notification.create_for_all_users(
|
|
db,
|
|
title=notification_in.title,
|
|
message=notification_in.message,
|
|
type=notification_in.type,
|
|
link=notification_in.link,
|
|
extra_data=notification_in.extra_data
|
|
)
|
|
return {"sent_to": count}
|