Сервис для многофункционального телеграм бота для аккаунтов из Яндекс Staff, предоставляющего бизнес-функции для внутреннних заказчиков и их внешних партнеров. Поверх сценариев бота доступны HTTP API на Gin (префикс /api/v1), JWT-авторизация, сессии в Redis, файлы в S3-совместном хранилище (MinIO), SMTP для писем сброса пароля, приём вебхуков Яндекс Форм и фоновая очистка «осиротевших» файлов.
- Авторизация через Staff аккаунт Яндекса с использованием ролевой модели;
- Преоставляет перечень коробочных решений по посещению мероприятий с возможностью выбора свободных дат и времени;
- Скачивание гайда по посещению мероприятий;
- Запрос спецпроекта;
- Просмотр примеров спец.проектов;
- Просмотр справочной информации о сервисе;
- Связь с поддержкой;
- HTTP API для админки и интеграций (
/api/v1): регистрация и вход, refresh-токены, выход, запрос и сброс пароля по e-mail (SMTP из конфига); - Коробочные решения в API: список, карточка, создание, изменение, удаление, загрузка изображения, смена статуса; проверка ролей и отдельных прав (создание/редактирование/удаление коробок);
- Спецпроекты в API: список, создание, просмотр, обновление, удаление с разграничением прав;
- Заявки на спецпроекты: список, создание оператором, просмотр, смена статуса, удаление;
- Бронирования: список, просмотр по id, смена статуса, удаление; отдельные права на просмотр/редактирование/удаление;
- Настройки сервиса: тексты сообщений бота; чтение и изменение матрицы прав по ролям (админ);
- Аналитика: выгрузка (эндпоинт export) при наличии права на просмотр аналитики;
- Страницы ресурсов (контент разделов бота — о сервисе, гайд, примеры, полезные ссылки, заявка на спецпроект): список, получение по slug, правка, загрузка файла к странице, удаление ссылок и страницы;
- Загрузка файлов через API (
POST /files/upload) в объектное хранилище; публичная выдача контента страницы ресурса без JWT (GET /api/v1/public/resources/:slug); - Пользователи Staff: список и карточка (админ); создание, обновление, смена статуса учётной записи; дашборд для роли менеджер/админ;
- Интеграция с Яндекс Формами: публичный
POST /api/v1/public/applications/принимает тело ответа формы; сверяются заголовкиX-Webhook-Token(секрет из конфига) иX-Form-Answer-Id; - Сессии бота и связанные сценарии в Redis; ограничение частоты обработки апдейтов и внешних вызовов (rate limiting в боте);
- MinIO (или другое S3-совместное API) для медиа и вложений; при старте создаётся bucket; фоновый воркер удаляет объекты в хранилище, на которые больше нет ссылок в БД (параметры
file_gc/FILE_GC_*); - Режим
API_ONLY: один процесс поднимает API, миграции, метрики и health без запуска Telegram-бота (удобно для разработки фронта); - CORS для браузерного фронта, метрики Prometheus и middleware на стороне Gin; маршруты собраны в
internal/api/server/routes.go.
- Введение
- Структура проекта
- Требования
- Конфигурация
- Быстрый старт
- Запуск (локально и Docker)
- HTTP API (
/api/v1) - Миграции базы данных
- Makefile цели
- Логи и метрики
- Разработка
- Тесты и линтинг
- Точки здоровья (health/endpoints)
- Траблшутинг
Сервис написан на Go, включает HTTP API, работу с БД и миграциями, логирование, метрики и процесс Telegram-бота. Фактически один исполняемый модуль собирается из cmd/bot: при старте выполняются миграции goose, подключаются PostgreSQL и Redis, инициализируется клиент к объектному хранилищу, поднимается Gin-сервер с CORS и JWT-middleware, отдельный HTTP-сервер для /metrics и /health на порту prometheus_port, опционально запускается бот и воркер очистки файлов. Конфигурация читается через Viper из config/config.yaml с переопределением из переменных окружения; для локальной разработки используйте .env.example → .env.
Запуск возможен локально, в Docker и через docker-compose, в том числе целями make run-local (полный стек, порты на 127.0.0.1) и make run-local-api (инфраструктура на localhost для go run на хосте).
├── main.go # Точка входа HTTP-сервиса (API)
├── cmd/
│ └── bot/
│ └── main.go # Точка входа отдельного процесса бота
├── internal/
│ ├── bot/ # Логика бота: обработчики, клиенты, воркеры
│ ├── handlers/ # HTTP-обработчики (роуты, контроллеры)
│ ├── models/ # Доменные модели и DTO
│ ├── database/ # Подключение к БД, репозитории
│ ├── config/ # Чтение и валидация конфигурации
│ ├── logger/ # Инициализация и обертки логирования
│ └── metrics/ # Метрики
├── migrations/ # SQL/скрипты миграций базы данных
├── config/
│ └── config.yaml # Основной конфигурационный файл
├── Dockerfile # Сборка Docker-образа приложения
├── docker-compose.yml # Сценарий запуска сервиса и зависимостей (БД и т.д.)
├── Makefile # Сценарии автоматизации (build, run, test, lint, migrate)
└── go.mod / go.sum # Модули и зависимости Go
В актуальной ветке main.go в корне может отсутствовать; единая точка входа — cmd/bot/main.go. Дополнительно в дереве кода:
internal/api/— Gin:server,handlers,middleware(аутентификация по JWT, CORS, метрики, проверка ролей);internal/repository/postgres,internal/repository/redis— доступ к данным и сессиям;internal/storage/minio— загрузка и удаление объектов в бакете;internal/worker/— фоновые задачи (очистка файлов);internal/service/иinternal/dto/— бизнес-логика и контракты API/бота;scripts/— обёртки./scripts/run,./scripts/stopнадmake;docker-compose.infra.yml,docker-compose.local.infra.yml,docker-compose.local.app.yml— база, Redis, MinIO и публикация портов для локальной разработки;.env.example— шаблон переменных для compose и хоста.
- Go 1.26+ — на компе нужна актуальная версия. Проверка:
go version. Установка: go.dev/dl. Для автоматической подстановки тулчейна: вgo.envили окружении задатьGOTOOLCHAIN=auto. Версия модуля в репозитории смотрите вgo.mod. - Docker 24+ и Docker Compose (для контейнерного запуска)
- Доступ к СУБД (например, PostgreSQL)
- Redis (сессии и сопутствующая логика)
- S3-совместное хранилище (в compose по умолчанию MinIO)
Базовая конфигурация хранится в config/config.yaml. Значения могут переопределяться через переменные окружения.
Пример содержимого config/config.yaml:
server:
port: 8080
readTimeout: 5s
writeTimeout: 5s
database:
maxOpenConns: 20
maxIdleConns: 5
connMaxLifetime: 30m
logger:
level: info
format: json
metrics:
enabled: true
endpoint: /metrics
bot:
enabled: true
token: "${BOT_TOKEN}" # можно переопределить через окружениеПеременные окружения (примеры):
APP_ENV=local|dev|prodBOT_TOKEN=...DB_DSN=...(доступ к БД, ссылка на запись в защищённом хранилище)LOG_LEVEL=debug|info|warn|error
Загрузчик конфигурации читает config.yaml, затем применяет ENV-опции.
В коде загрузка реализована через Viper (internal/config/config.go). Имеют значение, в частности:
- параметры PostgreSQL (
db.*/POSTGRES_*,DB_HOST_PORT); - Redis (
redis.*/REDIS_ADDR,REDIS_PASSWORD,REDIS_DB); - JWT и TTL токенов (
auth_config/JWT_SECRET); - MinIO: endpoint, ключи, имя бакета,
public_base_url(MINIO_*); - SMTP и базовый URL для ссылок в письмах (
email.*/SMTP_*,EMAIL_*); yandex_forms.webhook_token/YANDEX_FORMS_WEBHOOK_TOKEN(обязателен при старте приложения);file_gc— включение воркера, интервал, «период милосердия» для сирот, размер батча;api_only,cors, лимитыmsg_rps/api_rps,session.ttl,port,prometheus_port,migrations_dir.
Удобный старт для Docker и хоста: скопировать .env.example в .env и заполнить секреты.
- Убедитесь, что корректно заполнен
config/config.yaml - Запустите сервис:
- Локально:
go run ./main.go - В Docker:
docker compose up --build
- Локально:
- Для полного локального стека из репозитория:
cp .env.example .env, заполните токены и пароли, затемmake run-localили./scripts/run - Чтобы поднять только Postgres, Redis и MinIO на
127.0.0.1и гонять API на машине разработчика:make run-local-api, далее экспорт переменных из.env(в т.ч.DB_HOST_PORT,REDIS_ADDR, переменные MinIO) иgo run ./cmd/bot/*.go— см. комментарии в.env.example
- Запуск API:
go run ./main.go
- Запуск бота:
go run ./cmd/bot/main.go
Сейчас типичный запуск API и бота одной командой:
go run ./cmd/bot/*.goили make run. Только HTTP API и метрики без бота: задайте API_ONLY=true (и при необходимости пустой BOT_TOKEN не используется в этом режиме согласно логике конфига).
- Собрать образ:
docker build -t backend-service:latest . - Запустить контейнер:
docker run --rm -p 8080:8080 \ -e APP_ENV=prod \ -e BOT_TOKEN=... \ backend-service:latest
- Запуск сервиса и зависимостей:
docker compose up --build
- Остановка:
docker compose down
Для этого репозитория удобно пользоваться make run-local (инфра из docker-compose.infra.yml + приложение, порты проброшены на 127.0.0.1 через overlay-файлы), make stop-local или ./scripts/stop, make run-local-api — только инфраструктура на localhost. Подробности в Makefile и scripts/README.md.
Чтобы поднимать весь бэкенд (postgres, redis, bot) одной командой и задавать токен бота через файл:
- Скопируйте пример env-файла и укажите токен:
cp .env .env # отредактируйте .env: BOT_TOKEN=ваш_токен_от_BotFather - Запуск:
./scripts/run
- Остановка:
./scripts/stop
Скрипт run проверяет наличие .env и непустого BOT_TOKEN перед запуском. Логи бота: docker compose logs -f bot.
Примечание: убедитесь, что в docker-compose.yml настроены сервисы (например, db) и корректные переменные окружения. В полном стеке также поднимаются Redis, MinIO и сервис приложения с зависимостями по healthcheck.
- Без JWT: группа
/api/v1/auth— логин, регистрация, обновление и инвалидация сессии (refresh/logout), восстановление и смена пароля. - С JWT: защищённые группы с проверкой ролей Staff и набора прав (см. модели прав в
internal/models):/boxes— коробочные решения;/special-projects— спецпроекты;/applications— заявки;/bookings— бронирования;/settings— сообщения и права;/analytics— в т.ч. выгрузка;/resources— страницы ресурсов и файлы к ним;/files/upload— загрузка файла в хранилище;/users— список/карточка (админ) и отдельные админские маршруты создания/обновления/статуса;/dashboard— сводка для менеджеров и администраторов.
- Публично:
GET /api/v1/public/resources/:slug,POST /api/v1/public/applications/(вебхук форм; ответы сервиса не раскрывают ошибки валидации наружу).
Полный перечень методов и путей — в internal/api/server/routes.go.
Для управления миграциями используется goose. Миграции автоматически применяются при запуске приложения.
-
Создание миграции:
make migration-create NAME=название make migration-create NAME=create_users_table
-
Откат последней миграции:
make migration-rollback DB_DSN="postgres://user:pass@localhost:5432/db?sslmode=disable" -
Структура файла миграции:
-- +goose Up CREATE TABLE ...;
-- +goose Down DROP TABLE ...;
-
Команды Makefile:
- make migration — справка
- make migration-create NAME= — создать миграцию
- make migration-rollback DB_DSN= — откатить последнюю
-
Логи при запуске:
- Current database version: 20240321120000
- Found 2 pending migration(s)
- Applied 2 migration(s)
- Database version: 20240321120000 → 20240322123456
Помимо сборки и запуска:
build— бинарь в./bin/appизcmd/bot;run—go runтого же модуля;test— тесты с подробным выводом и покрытием;fmt/fix— форматирование и группировка импортов (goimports + gofmt);vet,lint,lint-fix— статический анализ;migration,migration-create,migration-rollback— миграции goose;run-local,run-local-api,stop-local— Docker compose для локальной разработки;run-frontend,run-frontend-local-api— npm-скрипты внешнего фронта (FRONTEND_DIR=...);generate-mocks— генерация моков;help— краткая справка по целям.
-
Логи:
- Конфигурируются через
internal/loggerиconfig.yaml. - Уровни логирования:
debug,info,error.
- Конфигурируются через
-
Метрики:
- Экспорт метрик Prometheus включается через
metrics.enabled. - Эндпоинт метрик задаётся
metrics.endpoint(например,/metrics). - В
internal/metricsобычно находятся регистраторы, middleware и хэндлеры.
- Экспорт метрик Prometheus включается через
Отдельно от основного API на порту PROMETHEUS_PORT (например, 9090) поднят mux с GET /metrics (Prometheus) и GET /health (пинг БД и проверка доступности Telegram API с кэшем). Основной REST API слушает SERVER_PORT (маршруты под /api/v1/...).
- Структура кода следует стандарту Go Modules.
- Бизнес‑логика и инфраструктурный код разделены по пакетам внутри
internal/. - Новые HTTP‑маршруты добавляйте в
internal/handlers. - Новые модели — в
internal/models. - Работу с БД (репозитории/ORM) — в
internal/database. - Конфиги и валидация — в
internal/config.
Для веб-API админки используйте internal/api/handlers и регистрацию в internal/api/server/routes.go; реализации доступа к данным — internal/repository/postgres, сессии — internal/repository/redis.
Рекомендации:
- Соблюдайте контракты интерфейсов для удобства тестирования.
- Используйте контекст
context.Contextво всех внешних вызовах. - Оборачивайте ошибки и логируйте с полями (structured logging).
- Запуск тестов:
go test ./... -race -cover - Линтинг (пример с golangci-lint):
golangci-lint run
- Форматирование:
gofmt -w . go mod tidy
Те же задачи можно вызывать через make test, make lint, make lint-fix, make fmt.
Рекомендуется:
/healthz— liveness probe (проверка, что процесс жив)./readyz— readiness probe (проверка готовности зависимостей, например БД)./metrics— метрики (если включено).
Порты и пути настраиваются через config.yaml.
В текущей реализации процесса приложения на порту prometheus_port доступны /health (JSON со статусом БД и Telegram) и /metrics; см. cmd/bot/main.go и internal/api/health.go.
- Проблема запуска:
- Проверьте
config/config.yamlи переменные окружения. - Убедитесь, что БД доступна и DSN корректен.
- Проверьте
YANDEX_FORMS_WEBHOOK_TOKEN: пустое значение не пройдёт валидацию конфига при старте.
- Проверьте
- Ошибки миграций:
- Удостоверьтесь, что инструмент миграций установлен.
- Сверьте права пользователя БД.
- Нет метрик:
- Проверьте
metrics.enabledи путьmetrics.endpoint. - Убедитесь, что порт
PROMETHEUS_PORTне занят другим процессом; проверка:curlнаhttp://127.0.0.1:${PROMETHEUS_PORT}/health.
- Проверьте
- Логи пустые или слишком многословные:
- Настройте
logger.levelиlogger.format.
- Настройте
- Ошибки Redis или MinIO:
- В Docker используйте хосты сервисов из compose (
redis,minio); приgo runна хосте —127.0.0.1и порты изdocker-compose.local.infra.yml(5432, 6379, 9000, консоль MinIO 9001).
- В Docker используйте хосты сервисов из compose (
- 401/403 в API:
- Проверьте заголовок авторизации, срок JWT и роль пользователя в Staff относительно требуемых прав маршрута.
pprof доступен на отдельном порту только локально.
Локально:
# Снять CPU профиль под нагрузкой (30 секунд)
go tool pprof 'http://localhost:6060/debug/pprof/profile?seconds=30'
# Heap (память)
go tool pprof -http=:8091 'http://localhost:6060/debug/pprof/heap'
# Горутины
go tool pprof -http=:8091 'http://localhost:6060/debug/pprof/goroutine'На проде — только через SSH туннель:
ssh -L 6060:localhost:6060 user@server
# затем локально:
go tool pprof 'http://localhost:6060/debug/pprof/profile?seconds=30'Порт 6060 проброшен только на 127.0.0.1 — снаружи недоступен.