Skip to content

Polling service with short links for voting and results. Go + Redis + React.

Notifications You must be signed in to change notification settings

AlexeyLars/surway

Repository files navigation

🎯 Surway

Современный full-stack сервис для создания и проведения опросов с множественным выбором

Легкий, быстрый и масштабируемый микросервис для создания временных опросов с автоматическим истечением срока действия. Построен на Go + Redis в backend и Next.js 15 + React 19 на frontend.


📋 Описание

Surway — это full-stack приложение для создания и проведения опросов (surveys/polls) с поддержкой множественного выбора вариантов. Проект использует Redis в качестве основного хранилища данных с нативной поддержкой TTL (Time To Live), что обеспечивает автоматическое удаление устаревших опросов и высокую производительность.

Ключевые особенности

  • Множественный выбор — пользователи могут выбрать несколько вариантов ответа
  • Временные опросы — автоматическое удаление по истечении TTL (от 7 до 30 дней)
  • Real-time результаты — мгновенное отображение результатов голосования
  • REST API с Swagger документацией
  • Атомарные операции — безопасный подсчет голосов через Redis pipeline
  • Graceful shutdown — корректное завершение работы сервера
  • Production-ready — Docker Compose + Caddy для SSL и reverse proxy
  • Современный UI — анимации, графики, адаптивный дизайн

🏗️ Архитектура

Проект следует принципам Clean Architecture и разделен на независимые слои:

surway/
├── backend/                    # Go backend
│   ├── cmd/
│   │   └── api/
│   │       └── main.go         # Точка входа с Swagger аннотациями
│   ├── internal/
│   │   ├── handler/            # HTTP handlers (Gin framework)
│   │   │   ├── poll.go         # CRUD операции для опросов
│   │   │   └── router.go       # Роутинг + middleware (CORS, logging)
│   │   ├── service/            # Бизнес-логика
│   │   │   ├── poll.go
│   │   │   └── poll_test.go    # Unit тесты
│   │   ├── storage/            # Слой данных
│   │   │   └── redis.go        # Redis реализация Storage интерфейса
│   │   ├── model/              # Модели данных
│   │   │   └── poll.go         # Poll, Request/Response структуры
│   │   ├── config/             # Конфигурация
│   │   │   └── config.go       # Загрузка из env переменных
│   │   └── lib/
│   │       └── random/         # Генерация коротких ID для опросов
│   │           └── random.go
│   ├── docs/                   # Swagger документация (auto-generated)
│   │   ├── docs.go
│   │   └── swagger.yaml
│   ├── Dockerfile              # Multi-stage build
│   ├── go.mod
│   └── go.sum
│
├── frontend/                   # Next.js 15 frontend
│   ├── app/                    # App Router (Next.js 15)
│   │   ├── page.tsx            # Главная страница
│   │   ├── layout.tsx          # Root layout
│   │   ├── create/             # Создание опроса
│   │   ├── [id]/               # Страницы опроса (динамический роутинг)
│   │   │   ├── page.tsx        # Голосование
│   │   │   └── results/        # Результаты
│   │   ├── config/             # Конфигурация приложения
│   │   └── services/           # API клиент
│   ├── components/             # React компоненты
│   │   ├── ui/                 # UI компоненты
│   │   └── analytics/          # Графики и визуализация
│   ├── public/                 # Статические файлы
│   ├── Dockerfile              # Multi-stage build для production
│   ├── package.json
│   └── next.config.js
│
├── configs/                    # Конфигурационные файлы
│   ├── redis.conf              # Настройки Redis для production
│   └── README.md
│
├── docker-compose.yml          # Development environment
├── docker-compose.prod.yml     # Production environment с Caddy
├── Caddyfile                   # Reverse proxy + автоматический SSL
├── Makefile                    # Команды для разработки
└── .env                        # Переменные окружения (не коммитится)

Компоненты системы

Backend (Go)

  • Handler Layer — обработка HTTP запросов, валидация, маппинг ошибок
  • Service Layer — бизнес-логика, генерация ID, создание URL
  • Storage Layer — работа с Redis через интерфейс
  • Middleware — CORS, структурированное логирование (slog), recovery
  • Swagger — автогенерация OpenAPI документации

Frontend (Next.js)

  • Server Components — для SEO и производительности
  • Client Components — для интерактивности
  • API Service — централизованный клиент для работы с backend
  • Recharts — визуализация результатов в виде графиков
  • Framer Motion — плавные анимации и переходы
  • Tailwind CSS 4 — современный стилинг

Storage (Redis)

Использует два типа структур данных:

  • String для метаданных опроса (JSON с TTL)
  • Hash для счетчиков голосов (атомарный HINCRBY)
  • Pipeline для атомарности создания/голосования

✨ Функциональность

API Endpoints

Метод Путь Описание
POST /api/v1/polls Создать новый опрос
POST /api/v1/polls/{id}/vote Проголосовать (множественный выбор)
GET /api/v1/polls/{id}/results Получить результаты опроса
GET /health Health check endpoint
GET /swagger/* Swagger UI документация

Примеры запросов

Создание опроса:

curl -X POST http://localhost:8080/api/v1/polls \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Какие языки программирования вы используете?",
    "options": ["Go", "Python", "JavaScript", "Rust", "TypeScript"]
  }'

Ответ:

{
  "poll_id": "abc123",
  "vote_url": "http://localhost:8080/api/v1/polls/abc123/vote",
  "results_url": "http://localhost:8080/api/v1/polls/abc123/results"
}

Голосование (множественный выбор):

curl -X POST http://localhost:8080/api/v1/polls/abc123/vote \
  -H "Content-Type: application/json" \
  -d '{
    "option_indices": [0, 2, 4]
  }'

Получение результатов:

curl http://localhost:8080/api/v1/polls/abc123/results

🚀 Быстрый старт

Требования

  • Docker 20.10+ и Docker Compose v2
  • Или локально:
    • Go 1.24.2+
    • Node.js 20+
    • Redis 7+

Запуск через Docker Compose (рекомендуется)

  1. Клонируйте репозиторий:

    git clone https://github.com/AlexeyLars/surway.git
    cd surway
  2. Запустите все сервисы:

    make docker-up
    # или
    docker compose up -d
  3. Доступ к приложению:

  4. Просмотр логов:

    make docker-logs
    # или
    docker compose logs -f
  5. Остановка:

    make docker-down
    # или
    docker compose down

Локальная разработка

Backend

  1. Настройте переменные окружения:

    cd backend
    cp .env.example .env  # если есть, или создайте .env
  2. Установите зависимости:

    make deps
    # или
    go mod download
  3. Запустите Redis:

    docker run -d -p 6379:6379 redis:7-alpine
  4. Запустите backend:

    make run
    # или
    go run cmd/api/main.go
  5. Сгенерируйте Swagger документацию:

    go install github.com/swaggo/swag/cmd/swag@latest
    swag init -g cmd/api/main.go

Frontend

  1. Настройте переменные окружения:

    cd frontend
    cp env.example .env.local
  2. Установите зависимости:

    npm install
  3. Запустите dev сервер:

    npm run dev

⚙️ Конфигурация

Backend (переменные окружения)

Настройки сервера

Переменная Описание Значение по умолчанию
ENV Окружение (dev/prod) dev
SERVER_HOST Хост сервера 0.0.0.0
SERVER_PORT Порт сервера 8080
SERVER_READ_TIMEOUT Таймаут чтения 10s
SERVER_WRITE_TIMEOUT Таймаут записи 10s
SERVER_SHUTDOWN_TIMEOUT Graceful shutdown таймаут 5s
BASE_URL Базовый URL для генерации ссылок http://localhost:8080

Настройки Redis

Переменная Описание Значение по умолчанию
REDIS_HOST Хост Redis localhost
REDIS_PORT Порт Redis 6379
REDIS_PASSWORD Пароль Redis (пусто)
REDIS_DB Номер БД Redis 0

Настройки опросов

Переменная Описание Значение по умолчанию
POLL_DEFAULT_TTL TTL опроса по умолчанию 168h (7 дней)
POLL_MAX_TTL Максимальный TTL 720h (30 дней)

Frontend (переменные окружения)

Client-side (NEXT_PUBLIC_*):

  • NEXT_PUBLIC_API_PROTOCOL — http/https
  • NEXT_PUBLIC_API_HOST — localhost
  • NEXT_PUBLIC_API_PORT — 8080
  • NEXT_PUBLIC_API_VERSION — v1
  • NEXT_PUBLIC_FRONTEND_PROTOCOL — http
  • NEXT_PUBLIC_FRONTEND_HOST — localhost
  • NEXT_PUBLIC_FRONTEND_PORT — 3000

Server-side (для SSR):

  • API_INTERNAL_PROTOCOL — http
  • API_INTERNAL_HOST — backend
  • API_INTERNAL_PORT — 8080

Пример .env файла для backend

# Environment
ENV=dev

# Server
SERVER_HOST=0.0.0.0
SERVER_PORT=8080
BASE_URL=http://localhost:8080

# Redis
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_DB=0

# Polls
POLL_DEFAULT_TTL=168h
POLL_MAX_TTL=720h

🔧 Разработка

Makefile команды

make help              # Показать все доступные команды
make build             # Собрать backend бинарник
make run               # Запустить backend локально
make test              # Запустить тесты с race detector
make test-coverage     # Показать покрытие тестами
make docker-up         # Запустить Docker Compose
make docker-down       # Остановить Docker Compose
make docker-logs       # Показать логи контейнеров
make docker-build      # Собрать Docker образы
make fmt               # Форматировать Go код
make lint              # Запустить линтер
make deps              # Установить/обновить зависимости
make clean             # Удалить артефакты сборки

Тестирование

# Backend
cd backend
go test -v ./...                          # Все тесты
go test -v -race ./...                    # С race detector
go test -cover ./...                      # С покрытием
go test -coverprofile=coverage.out ./...  # Сохранить отчет
go tool cover -html=coverage.out          # Открыть HTML отчет

# Frontend
cd frontend
npm run test           # Jest тесты (если настроены)
npm run lint           # ESLint проверка

Линтинг

Backend:

# Установка golangci-lint
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest

# Запуск
make lint
# или
cd backend && golangci-lint run

Frontend:

cd frontend
npm run lint

Swagger документация

Swagger документация генерируется автоматически из аннотаций в коде:

cd backend
swag init -g cmd/api/main.go

Доступна по адресу: http://localhost:8080/swagger/index.html


🐳 Docker

Development (docker-compose.yml)

Включает:

  • redis — Redis 7 с persistence
  • backend — Go API сервер
  • frontend — Next.js приложение

Порты наружу:

  • 6379 (Redis)
  • 8080 (Backend)
  • 3000 (Frontend)

Production (docker-compose.prod.yml)

Включает дополнительно:

  • caddy — Reverse proxy + автоматический SSL от Let's Encrypt

Особенности production конфига:

  • Порты backend/frontend не выставлены наружу (только через Caddy)
  • Redis с кастомной конфигурацией (configs/redis.conf)
  • Автоматический SSL через Caddy
  • Restart policy: always

Настройка для production:

  1. Отредактируйте Caddyfile:

    your-domain.com {
        reverse_proxy frontend:3000
        handle /api/* {
            reverse_proxy backend:8080
        }
    }
    
  2. Обновите переменные в docker-compose.prod.yml:

    BASE_URL=https://your-domain.com/api
    NEXT_PUBLIC_API_HOST=your-domain.com
  3. Запустите:

    docker compose -f docker-compose.prod.yml up -d

Структура данных в Redis

Метаданные опроса (String с TTL):

Ключ: poll:{poll_id}:info
Значение: JSON с Poll структурой
TTL: 168h (по умолчанию)

Счетчики голосов (Hash с TTL):

Ключ: poll:{poll_id}:votes
Значение: Hash {
  "0": "15",  # индекс опции -> количество голосов
  "1": "42",
  "2": "8",
  ...
}
TTL: 168h (синхронизирован с info)

Пример:

# Получить метаданные
redis-cli GET "poll:abc123:info"

# Получить голоса
redis-cli HGETALL "poll:abc123:votes"

# TTL
redis-cli TTL "poll:abc123:info"

🛠️ Технологический стек

Backend

  • Go 1.24.2 — основной язык
  • Gin — web framework
  • go-redis/v9 — Redis клиент
  • cleanenv — загрузка конфигурации
  • swaggo/swag — генерация Swagger документации
  • slog — структурированное логирование (стандартная библиотека)
  • testify — тестирование

Frontend

  • Next.js 15.5.4 — React framework с App Router
  • React 19.1.0 — UI библиотека
  • TypeScript 5 — типизация
  • Tailwind CSS 4 — утилитарный CSS
  • Recharts 3 — графики и визуализация
  • Framer Motion 12 — анимации
  • lucide-react — иконки

Infrastructure

  • Redis 7 — основное хранилище данных
  • Docker & Docker Compose — контейнеризация
  • Caddy 2 — reverse proxy + SSL
  • Alpine Linux — базовые образы

🗺️ Roadmap

В разработке

  • Защита от повторного голосования (cookies/IP)
  • Rate limiting middleware
  • WebSocket для live обновления результатов
  • Темная тема UI
  • Экспорт результатов (CSV, PDF)

Планируется

  • Аутентификация и личные кабинеты
  • История созданных опросов
  • Кастомизация опросов (цвета, фон)
  • Аналитика и статистика
  • CI/CD pipeline (GitHub Actions)
  • Метрики и мониторинг (Prometheus + Grafana)
  • E2E тесты (Playwright)
  • Интеграционные тесты для API

🤝 Контрибьюция

Приветствуются любые предложения и улучшения!

Процесс контрибьюции:

  1. Fork репозитория
  2. Создайте feature-ветку от develop:
    git checkout develop
    git checkout -b feature/amazing-feature
  3. Сделайте изменения и commit:
    git commit -m 'feat: add amazing feature'
  4. Push в вашу ветку:
    git push origin feature/amazing-feature
  5. Откройте Pull Request в develop ветку

Стандарты кода:

  • Backend: следуйте Effective Go и запускайте make lint
  • Frontend: используйте ESLint конфигурацию проекта
  • Commits: используйте Conventional Commits
    • feat: — новая функциональность
    • fix: — исправление бага
    • docs: — документация
    • refactor: — рефакторинг
    • test: — добавление тестов
    • chore: — рутинные задачи

📄 Лицензия

Этот проект создан в образовательных целях.


👤 Автор

Alexey Lars


🔗 Полезные ссылки


Статус проекта: 🚀 Active Development

Создано с использованием Go, Next.js и Redis