← Back to Projects

Project

SupportDesk (Realtime Support + Widget)

Socket.IO support inbox with assignment, tickets, analytics, and an embeddable widget (script + iframe)

Role
Solo
Focus
Realtime messaging, Embeddable widget, Multi-instance scaling
Testing
Jest (claim race, auth, widget, rate limit), Playwright E2E (customer→agent flow), Invite acceptance flow
Key patterns
Realtime, NestJS, Socket.IO, Redis
  • NestJS
  • TypeScript
  • Prisma
  • Postgres
  • Redis
  • Socket.IO
  • React + Vite
  • Playwright

What it does

  • Agents handle customer chats in a real-time inbox (claim, assign, close)
  • Turns conversations into tickets and tracks status
  • Embeds a widget on any site with one script tag
  • Shows needs-attention when the last message is from the customer, plus analytics and CSV export
  • Scales Socket.IO across instances with the Redis adapter

Architecture

┌────────────────────────┐      ┌────────────────────────┐
│  Vite + React          │─────►│  NestJS API + Gateway  │
│  (Agent UI /inbox)     │      │  REST + Socket.IO      │
└───────────┬────────────┘      └───────────┬────────────┘
            │                               │
            │ widget.js                     │ Prisma ORM
            ▼                               ▼
┌───────────────────────┐      ┌──────────────────────────┐
│  Iframe Widget UI     │◄───► │  Postgres                │
│  /embed/widget        │      │  convos/messages/tickets │
└───────────────────────┘      └────────────┬─────────────┘
                                            │
                                            ▼
                                    ┌──────────────────┐
                                    │  Redis adapter   │
                                    └──────────────────┘

Reliability and guardrails

  • Room joins and sends go through guarded handlers, with JWT on the API and the socket
  • Needs-attention is computed from lastAgentMessageAt and lastCustomerMessageAt
  • Rate limits on REST and Socket.IO return 429 with Retry-After
  • Helmet headers, message sanitization, and an audit log sit on the write path
  • Health checks cover Postgres and Redis; production Compose runs migrations before listen

Key engineering decisions

  • Needs-attention is a SQL comparison, not a flag written on every message

    lastAgentMessageAt versus lastCustomerMessageAt keeps filtered inbox pages and totals correct.

  • Redis adapter for Socket.IO

    More than one API instance can share rooms without sticky sessions.

  • Customer widget in an iframe, loaded by one script tag

    The host page only loads widget.js. Sizing and open/close go through postMessage.

Failure modes handled

  • Two agents claim the same conversation — the claim is guarded, and Jest covers the race
  • Expired widget token — the widget creates a new customer session
  • Redis or Postgres down — the health check fails and the container does not stay up
  • Rate limit hit — the API returns 429 with Retry-After
  • Customer message with no agent reply — the inbox shows needs-attention

How to demo in 2 minutes

  1. 1docker compose up -d, then npm install
  2. 2Apply Prisma migrations and seed the demo inbox
  3. 3npm run dev — UI on :5173, API on :3000/api
  4. 4Sign in as agent@local.test and open the inbox
  5. 5Load the widget script, send a customer message, and reply as the agent
  6. 6Claim a conversation and open analytics

Links