Smart Search Gateway
API Gateway для Smart Search микросервиса на базе rk-boot и Fiber.
Архитектура
Gateway — stateless HTTP сервер, который:
- Принимает HTTP запросы от клиентов
- Валидирует JWT токены
- Проксирует запросы на backend service через gRPC
- Возвращает HTTP ответы клиентам
Устойчивость (Resilience):
- Circuit Breaker — защита от каскадных сбоев при недоступности backend
- Graceful Degradation — сервис продолжает работать даже если backend недоступен
- Автоматическое переподключение — восстановление связи с backend в фоне
- Health Checks — endpoints для мониторинга состояния сервиса
Технологии:
rk-boot/v2— фреймворк для bootstrapfiber/v2— HTTP фреймворкgrpc— коммуникация с servicejwt/v5— валидация токеновprotobuf— gRPC контрактыgobreaker— circuit breaker паттерн
Структура проекта
smart-search-gateway/
├── cmd/server/main.go # Точка входа
├── internal/
│ ├── handler/ # HTTP handlers
│ ├── middleware/ # Auth, RequestID, Error handling
│ ├── grpc/ # gRPC client
│ ├── dto/ # Request/Response DTOs
│ └── config/ # Конфигурация
├── api/proto/ # Proto файлы (shared)
├── pkg/
│ ├── jwt/ # JWT утилиты
│ └── errors/ # Error handling
├── config/
│ ├── boot.yaml # rk-boot config
│ └── config.yaml.example # Пример конфигурации
└── Makefile
Установка
Требования
- Go 1.24+
- protoc (для генерации proto)
- golangci-lint (для линтера)
Установка зависимостей
make install-deps
Работа с Proto файлами
Философия: Gateway коммитит готовые .pb.go файлы в pkg/pb/, но не хранит исходные .proto файлы.
Зачем:
- Не нужен
protocпри деплое - Быстрая сборка без дополнительных инструментов
- Явный контроль версий proto-контракта
- Чистая структура импортов
Структура:
gateway/
└── pkg/pb/ # Сгенерированные .pb.go (в git)
├── auth/
├── user/
├── invite/
├── request/
└── supplier/
Импорты в коде:
import (
authpb "git.techease.ru/Smart-search/smart-search-gateway/pkg/pb/auth"
userpb "git.techease.ru/Smart-search/smart-search-gateway/pkg/pb/user"
)
Когда обновлять:
Обновляйте proto только когда backend изменил контракты:
make proto
Эта команда:
- Копирует
.protoфайлы из../smart-search-back/api/proto/(единый источник истины) во временную.tmp/ - Исправляет
go_packageпути для gateway - Генерирует
.pb.goи_grpc.pb.goфайлы напрямую вpkg/pb/ - Удаляет временную
.tmp/
Коммит результата:
git add pkg/pb/
git commit -m "Update proto contracts from smart-search-back"
Важно: Для работы команды make proto репозиторий smart-search-back должен находиться рядом:
Work/
├── smart-search-back/ # Источник истины для .proto
└── smart-search-gateway/ # Хранит .pb.go в pkg/pb/
Конфигурация
Скопируйте config/config.yaml.example в config/config.yaml и настройте:
jwt:
secret: YOUR_JWT_SECRET_HERE
grpc:
service_addr: localhost:9091
timeout: 30s
Переменные окружения:
JWT_SECRET— секрет для JWT (обязательно)SERVICE_GRPC_ADDR— адрес backend service (по умолчанию: localhost:9091)LOG_LEVEL— уровень логирования (по умолчанию: info)
Запуск
Локальный запуск
make run
Docker
# Сборка образа
make docker-build
# Запуск через docker-compose
docker-compose up
API Endpoints
Authentication
POST /api/v1/register
Регистрация нового пользователя
Request:
{
"email": "newuser@example.com",
"password": "password123",
"name": "John Doe",
"phone": "+1234567890",
"inviteCode": 123456
}
Response:
{
"accessToken": "eyJhbGci...",
"refreshToken": "eyJhbGci..."
}
Status Codes:
201 Created— успешная регистрация400 Bad Request— невалидные данные409 Conflict— email уже существует412 Precondition Failed— инвайт-код невалидный/истёк
POST /api/v1/login
Вход в систему
Request:
{
"email": "user@example.com",
"password": "password123"
}
Response:
{
"accessToken": "eyJhbGci...",
"refreshToken": "eyJhbGci..."
}
POST /api/v1/refresh
Обновление токенов
Request:
{
"refreshToken": "eyJhbGci..."
}
Response:
{
"accessToken": "eyJhbGci...",
"refreshToken": "eyJhbGci..."
}
POST /api/v1/logout
Выход из системы
Headers:
Authorization: Bearer <access_token>
Response:
{
"success": true
}
GET /api/v1/validate
Валидация токена
Headers:
Authorization: Bearer <access_token>
Response:
{
"valid": true,
"userId": 123
}
User
Все эндпоинты требуют Authorization: Bearer <access_token>
POST /api/v1/user/info
Получить информацию о пользователе
Response:
{
"email": "user@example.com",
"name": "John Doe",
"phone": "+1234567890",
"companyName": "ACME Corp",
"paymentStatus": "active"
}
POST /api/v1/user/balance
Получить баланс
Response:
{
"balance": 1000.50
}
POST /api/v1/user/statistics
Получить статистику
Response:
{
"totalRequests": 100,
"successfulRequests": 95,
"failedRequests": 5,
"totalSpent": 500.25
}
POST /api/v1/user/balance-statistics
Получить баланс и статистику
Response:
{
"balance": 1000.50,
"totalRequests": 100,
"totalSpent": 500.25
}
Invites
POST /api/v1/invite/generate
Сгенерировать инвайт-код
Request:
{
"ttlDays": 7,
"maxUses": 10
}
Response:
{
"code": "12345678",
"maxUses": 10,
"expiresAt": "2024-01-20T15:04:05Z"
}
POST /api/v1/invite/info
Получить информацию об инвайт-коде
Request:
{
"code": "12345678"
}
Response:
{
"code": "12345678",
"userId": 123,
"canBeUsedCount": 10,
"usedCount": 3,
"expiresAt": "2024-01-20T15:04:05Z",
"isActive": true,
"createdAt": "2024-01-13T15:04:05Z"
}
Requests
POST /api/v1/request/create-tz
Создать техническое задание
Request:
{
"requestTxt": "Нужны офисные столы деревянные, 10 шт"
}
Response:
{
"requestId": "uuid-here",
"tzText": "ТЕХНИЧЕСКОЕ ЗАДАНИЕ\n\n1. ПРЕДМЕТ\nПоставка офисных столов..."
}
POST /api/v1/request/approve-tz
Утвердить техническое задание
Request:
{
"requestId": "uuid-here",
"finalTz": "Окончательное ТЗ..."
}
Response:
{
"success": true,
"mailingStatus": "pending"
}
POST /api/v1/request/mailing-list
Получить список рассылок
Response:
{
"items": [
{
"requestId": "uuid-here",
"requestTxt": "Нужны офисные столы...",
"finalTz": "ТЕХНИЧЕСКОЕ ЗАДАНИЕ...",
"mailingStatus": "completed",
"createdAt": "2024-01-13T15:04:05Z",
"suppliersFound": 15
}
]
}
POST /api/v1/request/mailing-list-by-id
Получить рассылку по ID
Request:
{
"requestId": "uuid-here"
}
Response:
{
"item": {
"requestId": "uuid-here",
"requestTxt": "Нужны офисные столы...",
"finalTz": "ТЕХНИЧЕСКОЕ ЗАДАНИЕ...",
"mailingStatus": "completed",
"createdAt": "2024-01-13T15:04:05Z",
"suppliersFound": 15
}
}
Suppliers
POST /api/v1/suppliers/export-excel
Экспортировать список поставщиков в Excel
Request:
{
"requestId": "uuid-here"
}
Response: Binary file (application/vnd.openxmlformats-officedocument.spreadsheetml.sheet)
Error Handling
Все ошибки возвращаются в формате:
{
"error": "ERROR_CODE",
"message": "Human readable error message",
"request_id": "request-uuid"
}
Коды ошибок:
AUTH_INVALID_CREDENTIALS(401) — Неверный email или парольAUTH_MISSING_TOKEN(401) — Отсутствует токен авторизацииAUTH_INVALID_TOKEN(401) — Невалидный или истёкший токенINVITE_LIMIT_REACHED(429) — Достигнут лимит инвайт-кодовINVITE_INVALID_OR_EXPIRED(412) — Инвайт-код невалидный или истёкEMAIL_ALREADY_EXISTS(409) — Email уже зарегистрированINSUFFICIENT_BALANCE(402) — Недостаточно средствUSER_NOT_FOUND(404) — Пользователь не найденINVALID_REQUEST(400) — Невалидный запросINTERNAL_SERVER_ERROR(500) — Внутренняя ошибка сервераSERVICE_UNAVAILABLE(503) — Сервис недоступен
Разработка
Makefile команды
make proto # Генерация proto кода
make build # Сборка бинарника
make run # Запуск сервера
make lint # Запуск линтера
make test # Запуск тестов
make docker-build # Сборка Docker образа
make clean # Очистка сгенерированных файлов
Линтер
make lint
Тесты
make test
Мониторинг
Gateway поддерживает:
- Prometheus метрики (
:8080/metrics) - Request ID в каждом ответе (header
X-Request-ID) - Structured logging (JSON формат)
- OpenTelemetry tracing (Jaeger)
Health Check Endpoints
GET /health — Общее состояние сервиса
{
"status": "healthy", // или "degraded"
"backend": true // доступность backend
}
200— сервис полностью работоспособен503— сервис в деградированном режиме (backend недоступен)
GET /health/ready — Готовность к приему трафика
{
"status": "ready" // или "not_ready"
}
200— готов принимать запросы503— не готов (backend недоступен)
GET /health/live — Жизнеспособность сервиса
{
"status": "alive"
}
200— процесс работает корректно
Circuit Breaker
Gateway использует circuit breaker для защиты от каскадных сбоев при проблемах с backend.
Конфигурация:
- Порог срабатывания: 3 запроса с 60% ошибок
- Таймаут открытого состояния: 30 секунд
- Интервал проверки: 10 секунд
- Попытки переподключения: каждые 5 секунд
Состояния:
- Closed — нормальная работа, все запросы проходят
- Open — слишком много ошибок, запросы блокируются
- Half-Open — тестирование восстановления backend
Поведение при недоступности backend:
{
"error": "SERVICE_UNAVAILABLE",
"message": "Backend service is temporarily unavailable. Please try again later.",
"request_id": "uuid"
}
Gateway не падает при старте, если backend недоступен. Он продолжает работать и автоматически восстанавливает соединение при появлении backend.
License
Proprietary