Files
app-service/backend/app/api/v1/notifications.py
matteoscrugli 8c4a555b88 Add comprehensive backend features and mobile UI improvements
Backend:
- Add 2FA authentication with TOTP support
- Add API keys management system
- Add audit logging for security events
- Add file upload/management system
- Add notifications system with preferences
- Add session management
- Add webhooks integration
- Add analytics endpoints
- Add export functionality
- Add password policy enforcement
- Add new database migrations for core tables

Frontend:
- Add module position system (top/bottom sidebar sections)
- Add search and notifications module configuration tabs
- Add mobile logo replacing hamburger menu
- Center page title absolutely when no tabs present
- Align sidebar footer toggles with navigation items
- Add lighter icon color in dark theme for mobile
- Add API keys management page
- Add notifications page with context
- Add admin analytics and audit logs pages

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 22:27:32 +01:00

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."""
metadata = None
if db_obj.metadata:
try:
metadata = json.loads(db_obj.metadata)
except json.JSONDecodeError:
metadata = 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,
"metadata": metadata,
"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,
metadata=notification_in.metadata
)
return {"sent_to": count}