Skip to main content

Announcement Module

Location: backend/src/announcement/

A school-wide announcement board. Staff post notices that everyone in their school can read; the module also tracks per-user read receipts that power an unread badge and "read by" avatars on the frontend.

Files​

FilePurpose
announcement.module.tsModule definition
announcement.controller.tsAPI endpoints
announcement.service.tsBusiness logic, read tracking, caching
transformer.tsVersioned response shapes
dto/create-announcement.dto.tstitle (required), body (optional)
dto/update-announcement.dto.tsPartial update

Data model (public schema)​

TableColumns
announcementid, school_id, author_user_profile_id, title, body, created_at, updated_at
announcement_read(announcement_id, user_profile_id) PK, read_at

RLS: everyone in the school reads announcements; members may insert; author-or-admin may update/delete. Read receipts are visible to the school and writable only for oneself. The backend uses the service client and enforces authorization in code (RLS is the secondary defense).

Permissions​

announcement is a resource in the permission catalog with create / read / update / delete. Defaults: admins all, teachers full control, members read-only. The catalog syncs to public.permission_catalog on boot, so the resource shows up in the role editor automatically.

Endpoints​

All under AuthGuard + PermissionGuard. :id mutations additionally require the caller to be the author or a school admin (enforced in the service, not by the permission alone).

MethodRoutePermissionNotes
GET/announcementsannouncement:readSchool feed, newest first, with readers[] merged in
GET/announcements/unread-countannouncement:read{ count } of others' posts not yet read
POST/announcements/mark-readannouncement:readRecords a read receipt for every in-school announcement
GET/announcements/:idannouncement:read
POST/announcementsannouncement:createAuthor = caller
PATCH/announcements/:idannouncement:updateAuthor/admin only
DELETE/announcements/:idannouncement:deleteAuthor/admin only

Read tracking​

A read is recorded when a user opens the board (mark-read bulk-upserts a receipt per announcement) - a board-level "seen", not per-message. getUnreadCount returns announcements posted by others that the caller hasn't read. The author is never counted as unread on, nor shown as a reader of, their own post.

Caching​

Announcement content (title/body/author) is cached per school under announcements:content:<schoolId> (30-day TTL) and invalidated only on create/update/delete. Read receipts are fetched live on each list and merged into the cached content, so "read by" stays accurate without invalidating the content cache every time someone reads.