CougPulse is a campus monitoring platform with two connected experiences:
- Student heatmap: a live map showing room noise levels.
- Security operations: officer/admin tools for device management, face enrollment, alerting, and layout planning.
- Next.js App Router frontend + API routes (
src/app) - Prisma + PostgreSQL persistence (
prisma/schema.prisma) - Face recognition model assets in
public/models - Scalar-powered API docs at
/api/docs - Marp presentation source in
slides/presentation.md
/or/map— student heatmap/login— admin/security officer login/admin— security console (faces, alerts, devices, users, device accounts)/admin/layout— floor-plan and room editor/device— device client login + camera/mic reporting/api/docs— Scalar API reference/api/openapi.json— OpenAPI document
- Live noise heatmap sourced from room-level device readings
- Device heartbeat endpoint with audio level + preview image updates
- Face enrollment and matching (
/api/identify) usingface-api.jsdescriptors - Troublemaker flag workflow with persistent security alerts
- Floor/room authoring with polygon room geometry
- Separate admin and device authentication flows
- Encrypted-at-rest sensitive fields and Argon2 password hashing
- Next.js
16+ React19 - TypeScript
- Prisma
7with PostgreSQL (@prisma/adapter-pg+pg) - Tailwind CSS
4 face-api.js- Scalar (
@scalar/nextjs-api-reference) - Marp CLI (slides)
- Node.js
20+ - npm
10+ - PostgreSQL
15+(local or containerized)
npm installdocker compose up -dThe included compose.yaml provisions:
- database:
cougpulse - user:
cougpulse - password:
cougpulse - port:
5432
Create .env at the repo root:
DATABASE_URL="postgresql://cougpulse:cougpulse@localhost:5432/cougpulse"
ENCRYPTION_KEY="<64-hex-char-key>"Generate a key (PowerShell):
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"npx prisma migrate devnpm run devThe dev server runs on https://0.0.0.0:3000 (see package.json, --experimental-https).
On an empty database, the first admin login attempt auto-creates:
- username:
admin - password:
password
You should change this immediately from the admin user tools.
- Admin token header:
x-admin-token - Device token header:
x-device-token - Admin token local storage key:
cougpulse_admin_token - Device token local storage key:
cougpulse_device_auth_token
- Admin auth:
/api/auth/login,/api/auth/me,/api/auth/logout - Device auth:
/api/device-auth/login,/api/device-auth/me,/api/device-auth/logout - Layout & heatmap:
/api/floors,/api/floors/[id],/api/rooms,/api/rooms/[id],/api/layout/rooms - Faces & identify:
/api/faces,/api/faces/[id],/api/identify - Devices:
/api/devices,/api/devices/[id],/api/devices/[id]/heartbeat - Accounts & officers:
/api/device-accounts,/api/device-accounts/[id],/api/admin/users,/api/admin/users/[id] - Alerts:
/api/alerts,/api/alerts/[id]
Interactive docs are available at /api/docs.
- Admin and device passwords are hashed with Argon2id.
- Subject identity payloads are encrypted with AES-256-GCM.
- Sensitive fields (alert payloads, device preview/name, and secure notes) are stored encrypted.
- Login endpoints include in-memory attempt throttling per user/IP key.
Normal iterative workflow:
npx prisma migrate devCross-platform hard reset (drops data, reapplies migrations):
npx prisma migrate reset --forceBash-only helper script is available at scripts/reset-db.sh.
- Log in at
/login(admin/security officer). - Create device accounts from
/admin. - Sign devices in at
/device. - Assign each paired device to a room.
- Build floors/rooms in
/admin/layout. - Student view (
/) reflects live room noise. - Enroll faces and flag troublemakers; alerts appear when matched.
npm run dev
npm run build
npm run start
npm run lintSlides live in slides/presentation.md.
npm run slides:dev
npm run slides:html
npm run slides:pdfslides:pdf requires a local browser (Chrome/Edge/Firefox).
- Camera/mic blocked: use the HTTPS dev URL and allow browser permissions.
- Face models not loading: confirm files exist under
public/models. - Database connection errors: verify
DATABASE_URLand that PostgreSQL is running. - Encryption errors: ensure
ENCRYPTION_KEYis a valid 64-character hex string.