Skip to content

EPW80/Chat-Analytics-Platform

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

9 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Real-Time Chat Analytics Platform

A real-time, Twitch-style chat platform with a live analytics dashboard. A Go WebSocket backend (hub-and-spoke) handles rooms, message persistence, and metrics; a React + TypeScript frontend renders the chat and analytics in a polished dark UI.

Overview

The platform provides real-time room-based chat with live analytics: message throughput, active users vs. connections, peak connections, and broadcast-latency percentiles. Messages are persisted to DynamoDB through a bounded worker pool and exposed via a history API, while high-frequency metrics are tracked in memory with atomic counters and a sliding window.

Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Frontend β€” React + Vite + TypeScript        β”‚
β”‚   β€’ Virtualized chat (react-window)           β”‚
β”‚   β€’ Analytics dashboard (live polling)        β”‚
β”‚   β€’ useWebSocket: exponential-backoff reconnectβ”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
        wss /ws β”‚  https /api/*
                β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Hub (central manager)                        β”‚
β”‚   β€’ Clients grouped by room (map per RoomID)   β”‚
β”‚   β€’ Per-room broadcast fan-out                 β”‚
β”‚   β€’ Channel-based register/unregister/broadcastβ”‚
β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
        β”‚                           β”‚
        β–Ό                           β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Client connectionsβ”‚     β”‚ Analytics tracker     β”‚
β”‚ β€’ read/write pumpsβ”‚     β”‚ β€’ atomic counters     β”‚
β”‚ β€’ validation      β”‚     β”‚ β€’ 15-min sliding windowβ”‚
β”‚ β€’ rate limiting   β”‚     β”‚ β€’ latency percentiles β”‚
β”‚ β€’ ping/pong       β”‚     β”‚ β€’ GET /api/analytics  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
        β”‚ enqueue (non-blocking)
        β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Persistence worker pool                      β”‚
β”‚   β€’ bounded, batching (BatchWriteItem)         β”‚
β”‚   β€’ drops-when-full, drains on shutdown        β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   DynamoDB (chat-messages)                     β”‚
β”‚   β€’ PK RoomID / SK MessageID                   β”‚
β”‚   β€’ GSI: UserID-Timestamp, RoomID-Timestamp    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Features

  • Room-based chat β€” clients join a room (?room=, default global); broadcasts are scoped per room.
  • Live analytics β€” total messages, active connections vs. unique users, peak connections, messages/minute (15-min window), and p50/p95/p99 broadcast latency, served at /api/analytics.
  • Message history API β€” recent room history and per-user history, with join-time hydration so a connecting client replays recent messages.
  • Bounded persistence pool β€” messages are enqueued non-blocking and written to DynamoDB in batches by a fixed worker pool, keeping the broadcast path off storage latency.
  • Per-connection rate limiting β€” token-bucket throttle on inbound messages.
  • Token auth β€” optional HMAC-signed bearer tokens (enabled when AUTH_SECRET is set); falls back to a userId query param for local development.
  • Configurable CORS / WebSocket origin allowlist.
  • Graceful degradation β€” runs without DynamoDB (chat + live analytics still work, no persistence).
  • Polished React frontend β€” refined dark theme, design tokens, reusable UI primitives, avatars, virtualized message list, and a live metrics dashboard.
  • Comprehensive tests β€” every backend package has a _test.go; the suite runs clean under -race.

Quick Start

Full stack via Docker Compose

Prerequisites: Docker + Docker Compose, Git.

git clone https://github.com/EPW80/Chat-Analytics-Platform.git
cd Chat-Analytics-Platform

# Start DynamoDB Local + backend + frontend
docker-compose up -d

# Create DynamoDB tables (enables persistence + history)
./scripts/init-tables.sh

# Health check
curl http://localhost:8080/health
# {"status":"ok","clients":0,"storage":"ok"}

Services:

  • Frontend: http://localhost:3000
  • Backend: http://localhost:8080 (WebSocket at ws://localhost:8080/ws)
  • DynamoDB Local: http://localhost:8000

./start.sh orchestrates the same flow (build, wait for health, init tables).

Backend only (local Go)

Prerequisites: Go 1.23+.

cd backend
go mod download
go run ./cmd/server
# Runs on :8080. Without DynamoDB reachable it logs a warning and
# continues with storage disabled (chat + analytics still work).

Frontend only (Vite dev server)

Prerequisites: Node 20+.

cd frontend
npm install
npm run dev          # http://localhost:5173

The frontend reads VITE_WS_URL and VITE_API_URL (defaults point at localhost:8080). For a production build these are baked in at build time.

API

GET /health

Liveness + readiness. storage is ok, unavailable, or disabled.

{ "status": "ok", "clients": 5, "storage": "ok" }

WS /ws

WebSocket chat endpoint.

Query param Default Notes
userId anonymous ignored when token auth is enabled
username Anonymous display name
room global room to join
token β€” required when AUTH_SECRET is set (or Authorization: Bearer)
ws://localhost:8080/ws?userId=user123&username=Alice&room=global

GET /api/analytics

Point-in-time metrics snapshot.

{
  "totalMessages": 1280,
  "activeConnections": 4,
  "activeUsers": 3,
  "peakConnections": 17,
  "messagesPerMinute": [/* last 15 minutes */],
  "latencyP50Ms": 0.4, "latencyP95Ms": 1.2, "latencyP99Ms": 2.1,
  "activeUserDetails": [{ "clientId": "...", "userId": "...", "username": "Alice", "joinedAt": "..." }],
  "uptimeSeconds": 3600, "serverStartTime": "..."
}

GET /api/rooms/{id}/messages Β· GET /api/users/{id}/messages

Recent message history for a room or a user. Optional ?limit= (default 50, max 200). Returns 503 when storage is unavailable.

Message format

{
  "messageId": "550e8400-e29b-41d4-a716-446655440000",
  "roomId": "global",
  "type": "chat",
  "userId": "user123",
  "username": "Alice",
  "content": "Hello world",
  "timestamp": "2026-06-16T10:30:00Z"
}

Types: chat, system, join, leave. Validation: username required (≀50 chars); chat content required (≀1000 chars); messageId/timestamp/roomId are server-authoritative.

Project Structure

RealTimeChatAnalyticsPlatform/
β”œβ”€β”€ backend/                     # Go WebSocket server
β”‚   β”œβ”€β”€ cmd/server/              # Entry point, HTTP routes, wiring
β”‚   └── pkg/
β”‚       β”œβ”€β”€ analytics/           # Atomic counters, sliding window, /api/analytics
β”‚       β”œβ”€β”€ auth/                # HMAC-signed token authenticator
β”‚       β”œβ”€β”€ client/              # Per-connection read/write pumps
β”‚       β”œβ”€β”€ config/              # Environment configuration
β”‚       β”œβ”€β”€ hub/                 # Room-based connection manager
β”‚       β”œβ”€β”€ message/             # Message types + validation
β”‚       β”œβ”€β”€ persist/             # Bounded batching persistence worker pool
β”‚       β”œβ”€β”€ ratelimit/           # Per-connection token bucket
β”‚       └── storage/             # DynamoDB repository (interface-based)
β”œβ”€β”€ frontend/                    # React + Vite + TypeScript + Tailwind
β”‚   └── src/
β”‚       β”œβ”€β”€ components/          # Chat, message list, input, user list, dashboard
β”‚       β”‚   └── ui/              # Reusable primitives (Button, Card, Badge, …)
β”‚       β”œβ”€β”€ hooks/               # useWebSocket, useAnalytics, useElementSize
β”‚       β”œβ”€β”€ lib/                 # cn, userColor helpers
β”‚       └── types/               # Shared TypeScript types
β”œβ”€β”€ scripts/                     # init-tables.sh, wait-for-dynamodb.sh, …
β”œβ”€β”€ docker-compose.yml           # Local stack (DynamoDB + backend + frontend)
β”œβ”€β”€ start.sh                     # One-command local startup
└── docs/BUILD_PLAN.md           # Full architecture & phase plan

Configuration

Backend (environment variables):

Variable Default Purpose
PORT 8080 HTTP/WS port
LOG_LEVEL info debug / info / warn / error
DYNAMODB_ENDPOINT http://localhost:8000 local endpoint; empty/AWS for production
DYNAMODB_REGION us-east-1 DynamoDB region
AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY dummy local creds; use an IAM role in production
ALLOWED_ORIGINS * CORS + WebSocket origin allowlist (comma-separated)
AUTH_SECRET β€” HMAC secret; empty disables token auth
RATE_LIMIT_PER_SEC / RATE_LIMIT_BURST 5 / 10 per-connection token bucket (<=0 disables)
PERSIST_WORKERS / PERSIST_BATCH_SIZE / PERSIST_QUEUE_SIZE 4 / 25 / 1024 persistence pool tuning

Frontend: VITE_WS_URL (WebSocket URL) and VITE_API_URL (REST base), baked in at build time.

Development

cd backend
go test ./... -race      # all packages, race detector
go test ./... -cover     # coverage
go build ./cmd/server    # build the binary

cd ../frontend
npm run build            # tsc type-check + vite build
npm run lint             # eslint

Note: TestClient_PingPong has a documented pre-existing race in the test harness β€” left as-is intentionally.

Deployment

Production target: AWS ECS Fargate for the backend behind an ALB, DynamoDB (on-demand) for persistence, and the static frontend on S3 + CloudFront. The DynamoDB table and IAM policies are authored by hand. The backend uses the AWS default credential chain (task IAM role) when DYNAMODB_ENDPOINT is empty; ALLOWED_ORIGINS must list the public frontend origin and TLS (wss) is terminated at the edge.

Technology Stack

Backend: Go 1.23, gorilla/websocket, aws-sdk-go-v2 (DynamoDB), google/uuid, slog structured logging, goroutines + channels.

Frontend: React 18, Vite 5, TypeScript 5, Tailwind CSS 3, react-window (virtualization), lucide-react (icons), Inter (font).

Infrastructure: Docker + Docker Compose, DynamoDB Local (dev) / AWS DynamoDB (prod).

Security Notes

Implemented: token (HMAC) auth, configurable CORS/origin allowlist, per-connection rate limiting, server-authoritative message fields. For production also ensure: TLS/wss at the edge, a strong AUTH_SECRET via a secrets manager, an IAM task role (no static keys), and a restrictive ALLOWED_ORIGINS.

License

MIT

Author

Erik Williams (@EPW80)

About

Twitch-style chat analytics platform

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors