05 / WORK / CASE STUDY
NKUST Alumni Association Platform
The official alumni site — member directory, alumni companies and job board. In development since 2024, now running on the university domain.
- TYPE
- FULL STACK
- SCOPE
- In production · full stack + ops
- STACK
- DJANGO · DRF · MYSQL · REDIS · REACT
- LINKS
- LIVE
01 / PROBLEM
The alumni association had no unified platform: rosters, partner companies, job posts and announcements were scattered and manually maintained. This system folds 'alumni self-service → admin review → public display' and company self-service job/product listings into one platform, plus public pages for distinguished alumni and the department.
02 / CONSTRAINTS
- Public on a university domain, holding alumni PII under data-protection law — security expectations are high.
- Single-server deployment (nginx + gunicorn), no orchestration; ops, security and UX audits all fall on one person.
- The frontend is CRA (no SSR), yet an alumni site must be indexable by search engines.
03 / ARCHITECTURE
A Django 5 + DRF monolith (11 business apps, 39 models, 38 resource routes plus 118 custom actions) with a React 18 SPA. Authorization is deny-by-default — IsAuthenticated globally, public endpoints explicitly opted in; JWT rides in HttpOnly cookies; Redis caches hot queries.
A custom monitoring subsystem: middleware writes requests, errors and performance metrics into seven models, GeoIP tags origin countries, anomalies alert by email; logs rotate daily with gzip and a one-year retention — recording continuously since May 2025.
04 / RESPONSIBILITIES
Independent full-stack development and operations since 2024/08 (169 commits across both repos): data models, APIs, security hardening, the React frontend, deployment and monitoring.
05 / CHALLENGES → SOLUTIONS
CHALLENGE
A pre-launch security review surfaced serious flaws: privilege escalation via the registration API, serializer mass assignment, and member PII enumerable through sequential IDs.
SOLUTION
The root cause was global AllowAny with per-endpoint locking. Authorization was flipped to deny-by-default and a numbered program (SEC-001–030) fixed items one by one — field whitelists, owner filtering, sensitive-field redaction, uniform responses against enumeration. 29 fixes landed, backed by privilege/IDOR regression tests (7/7 passing).
CHALLENGE
A CRA SPA is hostile to crawlers by default, and off-the-shelf prerenderers shipped a puppeteer too old to use.
SOLUTION
Wrote a custom prerender script (puppeteer + Chrome for Testing): after build it renders 14 public routes back to static HTML, wired into postbuild; the backend serves a dynamic sitemap — fixing, along the way, a field bug that had it returning 500s.
CHALLENGE
No budget for an APM, yet a public site needs to know who is hitting it and what is slowing down.
SOLUTION
Built it in: middleware logs requests/errors/perf, GeoIP tags origins, scoped throttling (login 10/min, registration 10/hr) blocks abuse. The daily-rotated logs accumulating since May 2025 are direct evidence of real operation.
06 / SYSTEM FACTS
11
business apps
100+
API endpoints
30
audit findings (29 fixed)
43
Playwright E2E specs
07 / LESSONS
Start from default-deny. Global AllowAny with per-endpoint locking opened a whole attack surface — remediating before launch cost far more than doing it right from the start.
CRA plus a hand-rolled prerenderer works, but it is fragile — routes are maintained by hand and pinned to a Chrome build. Next time I would pick a framework with native SSR/SSG and delete the whole pipeline.
08 / SCREENS
All screens below show demo (mock) data — no real user data.



NEXT
NKUST Equipment Borrowing System