announcements.py 2.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465
  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 ``visible: false`` and an
  5. empty list rather than a 403. ``visible`` is separate from the list because the
  6. sidebar entry is there for whoever may see announcements, also while nothing is
  7. published, and hidden for everyone else.
  8. """
  9. from fastapi import APIRouter, Depends, HTTPException, status
  10. from sqlalchemy import select
  11. from sqlalchemy.ext.asyncio import AsyncSession
  12. from backend.app.core.auth import is_auth_enabled, require_auth_if_enabled
  13. from backend.app.core.database import get_db
  14. from backend.app.models.settings import Settings
  15. from backend.app.models.user import User
  16. from backend.app.services import announcements as service
  17. router = APIRouter(prefix="/announcements", tags=["announcements"])
  18. async def _may_see(db: AsyncSession, user: User | None) -> bool:
  19. if not await service.is_enabled(db):
  20. return False
  21. if not await is_auth_enabled(db):
  22. return True
  23. # Authenticated by API key: a script, not a person with an inbox.
  24. if user is None:
  25. return False
  26. if user.is_admin:
  27. return True
  28. all_users = (
  29. await db.execute(select(Settings.value).where(Settings.key == service.ALL_USERS_KEY))
  30. ).scalar_one_or_none()
  31. return (all_users or "").lower() == "true"
  32. @router.get("")
  33. async def list_announcements(
  34. db: AsyncSession = Depends(get_db),
  35. current_user: User | None = Depends(require_auth_if_enabled),
  36. ) -> dict:
  37. """Whether this user may see announcements, and the live ones newest first,
  38. with their read state."""
  39. if not await _may_see(db, current_user):
  40. return {"visible": False, "announcements": []}
  41. return {
  42. "visible": True,
  43. "announcements": await service.list_for(db, current_user.id if current_user else None),
  44. }
  45. @router.post("/{public_id}/read", status_code=status.HTTP_204_NO_CONTENT)
  46. async def mark_announcement_read(
  47. public_id: str,
  48. db: AsyncSession = Depends(get_db),
  49. current_user: User | None = Depends(require_auth_if_enabled),
  50. ) -> None:
  51. if not await _may_see(db, current_user):
  52. raise HTTPException(status.HTTP_404_NOT_FOUND, "Announcement not found")
  53. if not await service.mark_read(db, public_id, current_user.id if current_user else None):
  54. raise HTTPException(status.HTTP_404_NOT_FOUND, "Announcement not found")
  55. await db.commit()