campus_buddy: CampusBuddy: The LMS That Treats Classroom Chat Like Infrastructure

A Node, React, and PostgreSQL system that blends live messaging, role-based access, OTP onboarding, and moderation into one campus-wide workflow.

9 min read • View on GitHub • More from jeevasivan2006

A campus classroom rendered as a switchboard, with one message entering a central relay, a gate filtering it by role, and the approved message fanning out to a room while a storage drawer below holds the permanent record. The image explains that CampusBuddy is built around live delivery, permission checks, and persistence, not just static course pages.
CampusBuddy routes classroom communication through a single flow: check permissions, deliver immediately, then store the message for later retrieval.
Key Takeaways

The classroom is a live system, not a page

Most LMS products start with content. CampusBuddy starts with motion. A classroom message is not just posted, it is routed, checked, delivered, and saved in one pass, which makes the app feel closer to an operating layer than a course portal.

That choice matters because campus communication is messy. Students chat with each other, staff make official announcements, and admins need durable history. CampusBuddy treats those as different traffic classes instead of forcing them through one generic feed.

The most important mental model in CampusBuddy is the hybrid path: real-time delivery up front, then persistence and retrieval behind it.

Why the message flow is the whole story

In backend/server.js, Socket.io is not a sidecar. It is where the app decides who can talk, who should see the message, and whether the message deserves a notification. The server keeps a connectedUsers map, sends direct messages to a specific socket, and broadcasts classroom messages to a room.

socket.on('send_message', async (payload) => {
  const { roomId, senderId, message, type } = payload;

  // lookup connected user
  const recipientSocketId = connectedUsers.get(payload.recipientId);

  // persist first-class message history
  await pool.query(
    'INSERT INTO messages (room_id, sender_id, message, type) VALUES ($1, $2, $3, $4)',
    [roomId, senderId, message, type]
  );

  if (type === 'dm' && recipientSocketId) {
    io.to(recipientSocketId).emit('new_message', payload);
  }

  if (type === 'classroom') {
    io.to(`classroom_${roomId}`).emit('classroom_message', payload);
  }
});

The real move is not the socket event itself. It is the fact that persistence happens inside the same flow as delivery. That means the system does not have to choose between live chat and durable history. It gets both.

PatternStrengthTrade-off
REST-first chatSimple to reason about, easy to storeFeels laggy and passive
Socket-only chatFast and interactiveHistory can become fragile
CampusBuddy hybrid flowInstant delivery plus durable recordsMore logic in the transport layer
A close-up of three message paths crossing a checkpoint. One staff announcement lights a notification branch, one student-to-student chat bubble slides through quietly, and one blocked message stops at a gate. The image explains how the same transport behaves differently based on role and moderation checks.
CampusBuddy does not treat every classroom message the same. Staff announcements, peer chat, and blocked messages take different paths through the same transport.

Moderation is built into the transport layer

That second illustration is the key design clue. The app checks the classroom block list before it fans a message out, which means moderation is not a UI afterthought. It is part of delivery.

That choice keeps the system honest. If a user is blocked, the app does not merely hide a message after the fact. It stops the message at the gate.

ScenarioWhat CampusBuddy doesWhy it matters
Peer chat in a classroomDelivers the message without extra fan-outKeeps chatter lightweight
Staff announcementSends the message and triggers notificationsSignals authority clearly
Blocked senderStops the classroom message at the checkEnforces moderation before exposure

The trust stack starts before login

CampusBuddy does not wait until the dashboard to establish trust. OTP verification creates a first checkpoint, and the expiration logic keeps that checkpoint from becoming a loophole. After that, role middleware decides whether a user can reach staff, student, or admin routes.

That sequence is a good fit for campus software. A university app is always asking two questions at once: who are you, and what are you allowed to do here? CampusBuddy answers both early, then carries those answers through the rest of the experience.

// auth middleware sketch
if (!req.user) return res.status(401).json({ error: 'Unauthorized' });
if (!allowedRoles.includes(req.user.role)) {
  return res.status(403).json({ error: 'Forbidden' });
}

Classroom membership behaves like a state machine

The classroom controller makes the product feel organized instead of crowded. Staff create classrooms, students request to join, and the app separates joined rooms from rooms that are still available. That is not just a filter. It is a lifecycle.

Once you think in states, the UI makes more sense. A student is not simply looking at a list of classrooms. They are moving through join requests, membership, and participation, while staff hold the authority to accept and coordinate.

The schema tells the project’s age

The migration history suggests a project that kept growing in scope. Early tables handled the basics, then later scripts added avatars, notification settings, preferences, analytics, and richer user profiles. That is the footprint of a product that moved from prototype to platform.

There is a trade-off here. Raw SQL and many migrations give the team clarity and control, but they also demand discipline. The upside is visible in the repo structure: the data model is doing real product work, not just storing forms.

LayerEarly shapeLater shape
IdentityLogin and OTPRole-aware profiles and avatar support
CommunicationBasic messagesPersistent real-time classroom messaging
OpsSimple notificationsNotification preferences and analytics
Data accessCore queriesBroader schema evolution through migrations

What CampusBuddy is, and what it is not

CampusBuddy is not just another LMS, and it is not just a campus chat app. It sits between those categories. The point is coordination: who can speak, who needs to hear it, and how the system remembers what happened.

That makes it more interesting than a static course portal and more structured than a generic chat stack. It is a campus communication layer with rules, memory, and roles baked in.

Conventional LMS + chatCampusBuddy
Assignments, files, announcementsLive classroom messaging with durable history
Chat bolted on as a separate toolChat built into the delivery model
Permissions mostly at page levelPermissions enforced in transport and membership
Communication and records split apartCommunication and persistence tied together