Changelog
Notable changes to the codebase, grouped by date.
Each entry below links to a dedicated page with the full writeup - what changed, why, what to deploy, and any behavior changes downstream consumers should know about.
2026-06-04
- Announcement board - new school-wide notice board:
announcement+announcement_readtables with RLS, anannouncementRBAC resource,AnnouncementModule(CRUD + unread-count + mark-read), a/dashboard/announcementsboard with a permission-gated composer, a sidebar unread badge, and per-announcement read receipts (reader avatars with name tooltips). Content is cached per school; read receipts are merged in live. - Server-side report files - moved all report-file generation (student/year/report-card/exam PDFs, class-summary PDF/CSV/XLSX) from the browser to the backend
ReportFilesModule, which streams files to the client; added a bulkclass-zipthat streams a zip of every student's report card with flat memory; the frontend became a thin client and droppedjspdf/xlsx/@react-pdf/renderer/jszip. - Fixes & UI polish -
user_profile.emailcolumn + backfill, null-gender crash fixes (dashboard chart + edit form), a persistent class sidebar across class sub-pages, and a redesigned role permissions editor (toggle-pill rows replacing the fragile checkbox matrix). - Dependency maintenance - replaced the unmaintained, advisory-carrying
xlsx(SheetJS) with the actively maintained, write-onlywrite-excel-filefor XLSX report exports (the builders are now async); bumpedreact-day-picker9 → 10 (table→month_gridclassNames key); reconciled the rootreact/react-domoverride that was silently pinning the repo to19.2.5, unifying all workspaces on19.2.7; evaluatedarchiverv8 and deliberately stayed on v7 (v8 is a typeless ESM-only rewrite).
2026-05-29
- Security fixes - authorization and input-validation batch from a follow-up audit: attendance IDOR (update / delete / roster), avatar object IDOR + content-type spoofing, deactivated-user access, admin self-elevation via onboard/join, per-student calculation/report IDOR, and a spoofable rate-limit identity. Backend-only, no migrations.
2026-05-25
- Attendance tracking - new
student.attendance_recordtable + RLS,AttendanceModulewith mark/bulk-mark/update/delete and per-student range + summary reports, mark-attendance page under/dashboard/classes/[classId]/attendancewith a per-student report dialog. - Custom grade scales - new
grading.grade_scale+grade_scale_bandtables (one-default-per-school via partial unique index),GradeScaleModuleCRUD with admin-only writes, convert-on-read inGradeServiceso each grade returnsconverted: { label, gpaPoints, isPass } | null, admin settings page at/dashboard/grade-scales, and a band badge next to scores in the grading table. - Auth + dev ergonomics - incidental work that landed alongside the features:
useProfilerebuilt as anAuthProviderContext so the whole app shares one/auth/mefetch,SupabaseService.getUsermade non-throwing so stale refresh tokens don't crashAuthGuard, devstartscripts switched to Bun (the'bun'module crash), and dev-only throttle limits bumped (1000/min default, 100/hour auth-strict) so hot reloads don't trip 429s. Production limits unchanged. - Per-session throttling -
defaultthrottler now keys off the session (Bearer token, thensb-*-auth-tokencookie, then IP fallback) instead of the request IP, fixing 429s in production where every user behind a given Next.js middleware instance shared one IP-keyed bucket. Tokens are hashed before storage. Productiondefaultlimit also bumped from 300 to 10,000 req/60s as a temporary safety margin while a suspected frontend request-loop is investigated.
2026-05-24
- Critical security fixes - five criticals from the repo audit: cross-school admin RLS bypass,
assignment_writepolicy typo, IDOR in service-client endpoints, PDF upload arbitrary path, PDF download IDOR. - High-severity fixes - follow-up batch: removed
schoolIdfrom profile-update DTO, school-scoped theClassTeacherGuardadmin bypass, added auth to/calculations/student-*, closed the first-admin race with a partial unique index, fixed avatar upload path injection, hardened nginx config. - Medium-severity fixes - third batch: per-email rate limit on OTP/account-delete, school-mismatch check on enrollment, search-input sanitization in student.service,
leaveSchoolclears stale join requests, removed unused@supabase/supabase-jsfrom frontend, added CSP header to nginx. - Low-severity fixes - final cleanup:
BulkGradeDtopayload bounds, removedX-Server-Portdebug header, fixeddocker-compose.ymlnginx mount path, registered@fastify/cookiein the Cloudflare worker entrypoint, removed OTP-paste auto-submit, fixed double-decode on onboard/pending page. - Redis resilience against Upstash idle disconnects -
CacheServiceswallows Redis errors and degrades gracefully;RedisStorekeeps the connection alive with a 60sPING. - Infrastructure CI: Dockerfile & build workflow - added a multi-stage Bun Dockerfile for the backend, fixed the compose build context, and replaced the validation-only CI with three jobs (nginx syntax,
docker compose build, mock-upstream reverse-proxy test) wired up through small scripts in.github/scripts/.