2026-03-05 21:25:59 +02:00
|
|
|
# Scrum Solitare
|
|
|
|
|
|
2026-03-05 22:08:06 +02:00
|
|
|
Enterprise-style Scrum Poker application using Go, Gin, and SSE for real-time room updates.
|
|
|
|
|
|
|
|
|
|
## Highlights
|
|
|
|
|
|
|
|
|
|
- Go backend with layered architecture (`handlers`, `state`, `routes`, `config`)
|
|
|
|
|
- Memory-first room state with disk synchronization to JSON files
|
|
|
|
|
- Real-time updates via Server-Sent Events (SSE)
|
|
|
|
|
- Strict state sanitization: unrevealed votes from other users are never broadcast
|
|
|
|
|
- Backend authorization for admin-only actions (`reveal`, `reset`)
|
2026-03-05 22:30:37 +02:00
|
|
|
- Themeable frontend with:
|
|
|
|
|
- CSS architecture split into `main.css`, `layout.css`, and `/themes/*`
|
|
|
|
|
- Theme options: `win98` (default), `modern`, `No Theme`
|
|
|
|
|
- Light/Dark mode independent from active theme
|
|
|
|
|
- Desktop taskbar controls + mobile top controls for theme/mode switching
|
2026-03-05 22:08:06 +02:00
|
|
|
- Config page and card deck preview
|
|
|
|
|
- Drag-and-drop card ordering
|
|
|
|
|
- Card add/remove with animation completion handling
|
|
|
|
|
- Room password option
|
|
|
|
|
- Room interface with voting area, participants, and admin controls
|
|
|
|
|
- Username persistence through `localStorage`
|
2026-03-05 21:25:59 +02:00
|
|
|
|
2026-03-05 22:08:06 +02:00
|
|
|
## Project Layout
|
2026-03-05 21:25:59 +02:00
|
|
|
|
2026-03-05 22:08:06 +02:00
|
|
|
- `src/main.go`: app bootstrap
|
|
|
|
|
- `src/config/`: environment configuration
|
|
|
|
|
- `src/server/`: Gin engine setup
|
|
|
|
|
- `src/routes/`: page/api route registration
|
|
|
|
|
- `src/handlers/`: HTTP handlers (pages + API + SSE)
|
|
|
|
|
- `src/state/`: in-memory room manager, sanitization, persistence
|
|
|
|
|
- `src/middleware/`: static cache headers middleware
|
|
|
|
|
- `src/models/`: template page data models
|
|
|
|
|
- `src/templates/`: HTML templates (`index.html`, `room.html`)
|
2026-03-05 22:30:37 +02:00
|
|
|
- `static/css/main.css`: shared component primitives
|
|
|
|
|
- `static/css/layout.css`: grids/flex/positioning/responsive layout
|
|
|
|
|
- `static/css/themes/`: drop-in theme files
|
|
|
|
|
- `static/js/ui-controls.js`: global theme + mode engine
|
|
|
|
|
- `static/js/`: page logic (`config.js`, `room.js`)
|
2026-03-05 21:25:59 +02:00
|
|
|
|
2026-03-05 22:08:06 +02:00
|
|
|
## Environment Variables
|
2026-03-05 21:25:59 +02:00
|
|
|
|
2026-03-07 02:09:44 +02:00
|
|
|
### Quick Reference
|
|
|
|
|
|
|
|
|
|
- `HOST` (default: `0.0.0.0`): network interface/address the server binds to.
|
|
|
|
|
- `PORT` (default: `8002`): HTTP port the server listens on.
|
|
|
|
|
- `DATA_PATH` (default: `./data`): folder where room JSON files are stored.
|
|
|
|
|
- `MAX_ACTIVITY_LOG_ENTRIES` (default: `400`): max activity entries retained per room.
|
|
|
|
|
- `ADMIN_LOG_BROADCAST_LIMIT` (default: `200`): max recent log entries sent to admins over SSE.
|
|
|
|
|
- `STALE_ROOM_CLEANUP_INTERVAL` (default: `5m`): how often stale-room cleanup runs.
|
|
|
|
|
- `STALE_ROOM_TTL` (default: `30m`): empty rooms older than this are deleted.
|
|
|
|
|
|
|
|
|
|
### Variable Guide With Use Cases
|
|
|
|
|
|
|
|
|
|
`HOST`
|
|
|
|
|
- What it controls: where the web server is reachable from.
|
|
|
|
|
- Use case: local-only dev on one machine.
|
|
|
|
|
```bash
|
|
|
|
|
HOST=localhost PORT=8002 go run ./src
|
|
|
|
|
```
|
|
|
|
|
- Use case: allow access from LAN/Docker/k8s ingress.
|
|
|
|
|
```bash
|
|
|
|
|
HOST=0.0.0.0 PORT=8002 go run ./src
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
`PORT`
|
|
|
|
|
- What it controls: server port.
|
|
|
|
|
- Use case: avoid conflicts with another app already on `8002`.
|
|
|
|
|
```bash
|
|
|
|
|
PORT=9000 go run ./src
|
|
|
|
|
```
|
|
|
|
|
- Use case: common reverse-proxy port mapping.
|
|
|
|
|
```bash
|
|
|
|
|
HOST=0.0.0.0 PORT=8080 go run ./src
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
`DATA_PATH`
|
|
|
|
|
- What it controls: disk location for persisted rooms.
|
|
|
|
|
- Use case: keep data in a dedicated local directory.
|
|
|
|
|
```bash
|
|
|
|
|
DATA_PATH=./tmp/rooms go run ./src
|
|
|
|
|
```
|
|
|
|
|
- Use case: persistent mount in containers.
|
|
|
|
|
```bash
|
|
|
|
|
docker run --rm -p 8002:8002 -e DATA_PATH=/app/data -v $(pwd)/data:/app/data scrum-solitare
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
`MAX_ACTIVITY_LOG_ENTRIES`
|
|
|
|
|
- What it controls: per-room in-memory/disk activity history cap.
|
|
|
|
|
- Use case: retain more history for auditing/debugging.
|
|
|
|
|
```bash
|
|
|
|
|
MAX_ACTIVITY_LOG_ENTRIES=1000 go run ./src
|
|
|
|
|
```
|
|
|
|
|
- Use case: reduce memory/storage in lightweight deployments.
|
|
|
|
|
```bash
|
|
|
|
|
MAX_ACTIVITY_LOG_ENTRIES=100 go run ./src
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
`ADMIN_LOG_BROADCAST_LIMIT`
|
|
|
|
|
- What it controls: maximum recent activity entries included in admin SSE payloads.
|
|
|
|
|
- Use case: richer admin timeline in active rooms.
|
|
|
|
|
```bash
|
|
|
|
|
ADMIN_LOG_BROADCAST_LIMIT=400 go run ./src
|
|
|
|
|
```
|
|
|
|
|
- Use case: smaller SSE payloads for lower bandwidth.
|
|
|
|
|
```bash
|
|
|
|
|
ADMIN_LOG_BROADCAST_LIMIT=50 go run ./src
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
`STALE_ROOM_CLEANUP_INTERVAL`
|
|
|
|
|
- What it controls: scheduler frequency for checking stale empty rooms.
|
|
|
|
|
- Use case: aggressive cleanup in ephemeral environments.
|
|
|
|
|
```bash
|
|
|
|
|
STALE_ROOM_CLEANUP_INTERVAL=1m go run ./src
|
|
|
|
|
```
|
|
|
|
|
- Use case: reduce cleanup churn in stable environments.
|
|
|
|
|
```bash
|
|
|
|
|
STALE_ROOM_CLEANUP_INTERVAL=15m go run ./src
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
`STALE_ROOM_TTL`
|
|
|
|
|
- What it controls: time since last room activity before an empty room is deleted.
|
|
|
|
|
- Use case: auto-prune quickly for temporary team rooms.
|
|
|
|
|
```bash
|
|
|
|
|
STALE_ROOM_TTL=10m go run ./src
|
|
|
|
|
```
|
|
|
|
|
- Use case: keep empty rooms around longer between sessions.
|
|
|
|
|
```bash
|
|
|
|
|
STALE_ROOM_TTL=2h go run ./src
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Duration Format Notes
|
|
|
|
|
|
|
|
|
|
`STALE_ROOM_CLEANUP_INTERVAL` and `STALE_ROOM_TTL` use Go duration syntax:
|
|
|
|
|
- valid examples: `30s`, `5m`, `45m`, `2h`
|
|
|
|
|
- invalid examples: `5` (missing unit), `five minutes`
|
2026-03-05 21:25:59 +02:00
|
|
|
|
|
|
|
|
## Run Locally
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
go mod tidy
|
|
|
|
|
go run ./src
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Open `http://localhost:8002`.
|
|
|
|
|
|
2026-03-07 02:09:44 +02:00
|
|
|
Host binding examples:
|
2026-03-06 17:16:56 +02:00
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
HOST=localhost PORT=8002 go run ./src
|
|
|
|
|
HOST=0.0.0.0 PORT=8002 go run ./src
|
|
|
|
|
```
|
|
|
|
|
|
2026-03-07 01:19:37 +02:00
|
|
|
State tuning example:
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
MAX_ACTIVITY_LOG_ENTRIES=600 \
|
|
|
|
|
ADMIN_LOG_BROADCAST_LIMIT=300 \
|
|
|
|
|
STALE_ROOM_CLEANUP_INTERVAL=2m \
|
|
|
|
|
STALE_ROOM_TTL=45m \
|
|
|
|
|
go run ./src
|
|
|
|
|
```
|
|
|
|
|
|
2026-03-05 21:25:59 +02:00
|
|
|
## Docker
|
|
|
|
|
|
2026-03-05 22:08:06 +02:00
|
|
|
Build:
|
2026-03-05 21:25:59 +02:00
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
docker build -t scrum-solitare .
|
|
|
|
|
```
|
|
|
|
|
|
2026-03-05 22:08:06 +02:00
|
|
|
Run:
|
2026-03-05 21:25:59 +02:00
|
|
|
|
|
|
|
|
```bash
|
2026-03-05 22:08:06 +02:00
|
|
|
docker run --rm -p 8002:8002 -e DATA_PATH=/app/data scrum-solitare
|
2026-03-05 21:25:59 +02:00
|
|
|
```
|
|
|
|
|
|
2026-03-05 22:08:06 +02:00
|
|
|
## Real-Time Flow (SSE)
|
|
|
|
|
|
|
|
|
|
- Client joins room via `POST /api/rooms/:roomID/join`
|
|
|
|
|
- Client subscribes to `GET /api/rooms/:roomID/events?participantId=...`
|
|
|
|
|
- Server broadcasts sanitized room state updates on critical mutations:
|
|
|
|
|
- room creation
|
|
|
|
|
- join/leave
|
|
|
|
|
- vote cast
|
|
|
|
|
- reveal
|
|
|
|
|
- reset
|