Кейс · Бэкенд · TypeScript
poll-board: опросы в реальном времени
Сервис опросов и голосований на TypeScript без runtime-зависимостей. Опрос на 2–10 вариантов со сроком жизни, ссылка, результаты растут у всех в реальном времени по SSE или WebSocket (кадры WebSocket разобраны вручную). Повторные голоса и накрутка режутся подписанным токеном и хэшем IP; экспорт CSV защищён от формульных ячеек.
- TypeScript
- Node.js 24
- SSE
- WebSocket
- fast-check
- vitest
- axe
- Docker
- GitHub Actions
01 · Как выглядит
Скриншоты


02 · О проекте
Что это и зачем
Сервис опросов и голосований на TypeScript без runtime-зависимостей. Опрос на 2–10 вариантов со сроком жизни, ссылка, результаты растут у всех в реальном времени по SSE или WebSocket (кадры WebSocket разобраны вручную). Повторные голоса и накрутка режутся подписанным токеном и хэшем IP; экспорт CSV защищён от формульных ячеек.
Ограничения
Лимиты частоты и счётчики потоков живут в памяти одного процесса.
Автор опроса не аутентифицируется, только ключ закрытия.
Токен в cookie не защищает от человека, который чистит cookie и меняет IP.
Образ 244 МБ на node:24-alpine; проект работает на небольшом VPS с демо-данными.
Что внутри
- SSE и WebSocket на одном потоке событий; порядок монотонен: seq растёт на 1, closed последним
- 43 теста; fast-check: сумма голосов равна числу принятых бюллетеней, кадры WebSocket при любой нарезке потока
- 200 запросов (20 параллельно): p50 6,4 мс, p95 21,7 мс; около 25 МиБ RAM
- Запуск read-only, cap-drop ALL, uid 10001; axe 0 в Firefox на 390 и 1280
03 · Как запустить
Запуск одной командой
git clone <репозиторий по запросу> poll-board
cd poll-board
npm ci && npm run build && npm start04 · Код
Репозиторий приватный
Исходники лежат в приватном репозитории. Напишите в Telegram, открою доступ.
Запросить код