Voter is a robust, real-time web application designed to streamline meeting management. It allows participants to join via a WebSocket connection, raise hands with priority queuing (Topics vs. Replies), express reactions, and participate in parliamentary-style voting sessions initiated by administrators.
Built with a "Clean Architecture" mindset, this project leverages a modern TypeScript monorepo stack to ensure type safety, scalability, and maintainability.
- Real-time Interaction: Instant connection via WebSockets (Socket.io).
- Priority Queueing:
- Topics: Standard queue for new questions.
- Replies: Higher priority queue for direct responses to the current speaker.
- Automatic sorting based on type and timestamp.
- Reactions: Users can toggle reactions (e.g., "Like") to show agreement without interrupting.
- Voting Interface:
- Pop-up modal when a vote starts.
- Options: YES / NO / ABSTAIN.
- Session Management: Admins are recognized automatically based on configuration.
- Queue Control: Ability to lower individual hands or clear all reactions.
- Voting Control:
- Start Open (Public) or Anonymous voting sessions.
- Live progress tracking (voter count).
- Stop voting and reveal results globally.
The project is structured as a Monorepo using Turborepo to manage dependencies and build pipelines efficiently.
- Framework: Next.js (React, App Router)
- Styling: Tailwind CSS v4
- UI Components: shadcn/ui (built on Base UI) for a clean, accessible, and "boxy" aesthetic (Lyra theme).
- State Management: Custom React Hooks + Socket.io Client.
- Framework: NestJS
- Communication:
WebSocketGateway(Socket.io). - State: In-Memory Singleton Service (Clean separation of business logic and transport layer).
- Validation: Strict DTOs.
- A dedicated TypeScript package containing interfaces, types, and DTOs.
- Ensures Type Safety across the entire stack (Frontend & Backend share the exact same contracts).
- Docker: Multi-stage builds for optimized production images.
- Reverse Proxy: Nginx (serving Frontend) + Traefik (Edge Router & SSL).
.
├── apps
│ ├── backend # NestJS WebSocket Server
│ │ ├── src
│ │ │ ├── app.gateway.ts # Controller (WebSocket Events)
│ │ │ ├── app.service.ts # Domain Logic & State
│ │ │ └── ...
│ │ └── Dockerfile
│ │
│ └── frontend # Next.js Client
│ ├── src
│ │ ├── components # shadcn/ui & Feature components
│ │ ├── hooks # useGameState (Logic layer)
│ │ └── lib # Socket singleton
│ ├── nginx.conf # Production serving config
│ └── Dockerfile
│
├── packages
│ └── shared-types # The Contract between FE and BE
│ └── src
│ └── index.ts # Exported Interfaces (User, VoteSession, DTOs)
│
├── docker-compose.yml # Local production orchestration
└── turbo.json # Monorepo build pipeline config- Node.js (v20+)
- pnpm (Required for workspace management)
- Docker & Docker Compose (optional for local dev, required for prod)
Clone the repository and install dependencies using pnpm:
git clone <repo-url>
cd voter
pnpm installRun both the backend and frontend simultaneously in hot-reload mode:
pnpm dev- Frontend: http://localhost:5173
- Backend: http://localhost:3001
Create a .env file in apps/backend/ (or set environment variables in your deployment):
# Comma separated list of usernames who get Admin privileges
ADMINS=Viktor,Admin,Boss
PORT=3001The project is designed to be containerized. It uses Traefik as a reverse proxy for automatic SSL and routing.
To spin up the stack locally or on a VPS:
# 1. Build and start containers
docker compose up -d --build
# 2. Check logs
docker compose logs -fNote: The docker-compose.yml is configured to work with an external proxy_network and Traefik. Ensure Traefik is running if you use the default production compose file.
- Shared Types First: If you modify data structures, always update
packages/shared-typesfirst, then rebuild (pnpm build). - Clean Code: Keep the frontend "dumb" (display logic) and the hook/backend "smart" (business logic).
This project is open-source and available under the MIT License.