announcements.py 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960
  1. """Announcements from the Bambuddy maintainers (services/announcements.py).
  2. Shown to administrators, and to every signed-in user when the
  3. ``announcements_all_users`` setting is on. With authentication off whoever opens
  4. Bambuddy runs it, so they see them. Anyone else gets an empty list rather than a
  5. 403: the sidebar entry and the banner are hidden on "nothing to show", which is
  6. the same answer for "nothing published" and "not for you".
  7. """
  8. from fastapi import APIRouter, Depends, HTTPException, status
  9. from sqlalchemy import select
  10. from sqlalchemy.ext.asyncio import AsyncSession
  11. from backend.app.core.auth import is_auth_enabled, require_auth_if_enabled
  12. from backend.app.core.database import get_db
  13. from backend.app.models.settings import Settings
  14. from backend.app.models.user import User
  15. from backend.app.services import announcements as service
  16. router = APIRouter(prefix="/announcements", tags=["announcements"])
  17. async def _may_see(db: AsyncSession, user: User | None) -> bool:
  18. if not await service.is_enabled(db):
  19. return False
  20. if not await is_auth_enabled(db):
  21. return True
  22. # Authenticated by API key: a script, not a person with an inbox.
  23. if user is None:
  24. return False
  25. if user.is_admin:
  26. return True
  27. all_users = (
  28. await db.execute(select(Settings.value).where(Settings.key == service.ALL_USERS_KEY))
  29. ).scalar_one_or_none()
  30. return (all_users or "").lower() == "true"
  31. @router.get("")
  32. async def list_announcements(
  33. db: AsyncSession = Depends(get_db),
  34. current_user: User | None = Depends(require_auth_if_enabled),
  35. ) -> list[dict]:
  36. """Live announcements for this user, newest first, with their read state."""
  37. if not await _may_see(db, current_user):
  38. return []
  39. return await service.list_for(db, current_user.id if current_user else None)
  40. @router.post("/{public_id}/read", status_code=status.HTTP_204_NO_CONTENT)
  41. async def mark_announcement_read(
  42. public_id: str,
  43. db: AsyncSession = Depends(get_db),
  44. current_user: User | None = Depends(require_auth_if_enabled),
  45. ) -> None:
  46. if not await _may_see(db, current_user):
  47. raise HTTPException(status.HTTP_404_NOT_FOUND, "Announcement not found")
  48. if not await service.mark_read(db, public_id, current_user.id if current_user else None):
  49. raise HTTPException(status.HTTP_404_NOT_FOUND, "Announcement not found")
  50. await db.commit()