docs: update README, env example, and design docs
- backend/.env.example: add GEMINI_API_KEY and PEXELS_API_KEY placeholders - backend/Makefile: add test-integration to PHONY targets - backend/README.md: document external API keys, import/translate commands - docs/Design.md, docs/Tech.md: reflect Iteration 1 implementation and future plans Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# FoodAI Backend
|
||||
|
||||
Go REST API с авторизацией через Firebase, JWT и PostgreSQL.
|
||||
Go REST API с авторизацией через Firebase, JWT и PostgreSQL. Включает инфраструктуру импорта и перевода справочных данных (ингредиенты, рецепты).
|
||||
|
||||
## Стек
|
||||
|
||||
@@ -10,6 +10,8 @@ Go REST API с авторизацией через Firebase, JWT и PostgreSQL.
|
||||
- **goose** — миграции
|
||||
- **golang-jwt/v5** — JWT
|
||||
- **Firebase Admin SDK** — верификация токенов
|
||||
- **cobra** — CLI для команд импорта
|
||||
- **generative-ai-go** — Gemini API (переводы)
|
||||
|
||||
## Требования
|
||||
|
||||
@@ -37,6 +39,8 @@ cp .env.example .env
|
||||
| `JWT_REFRESH_DURATION` | Время жизни refresh-токена | `720h` |
|
||||
| `PORT` | Порт сервера | `8080` |
|
||||
| `ALLOWED_ORIGINS` | CORS-разрешённые источники | `http://localhost:3000` |
|
||||
| `SPOONACULAR_API_KEY` | Ключ Spoonacular API (нужен для команд `import`) | — |
|
||||
| `GEMINI_API_KEY` | Ключ Gemini API (нужен для команд `translate`) | — |
|
||||
|
||||
### 2. Запуск через Docker Compose
|
||||
|
||||
@@ -61,12 +65,14 @@ make run
|
||||
|
||||
## Команды
|
||||
|
||||
### Сервер и тесты
|
||||
|
||||
| Команда | Описание |
|
||||
|---|---|
|
||||
| `make run` | Запустить сервер в режиме разработки |
|
||||
| `make test` | Запустить unit-тесты |
|
||||
| `make test-integration` | Запустить интеграционные тесты (требует Docker) |
|
||||
| `make lint` | Проверить код через golangci-lint |
|
||||
| `make test` | Unit-тесты |
|
||||
| `make test-integration` | Интеграционные тесты (требует Docker) |
|
||||
| `make lint` | Проверка через golangci-lint |
|
||||
| `make docker-up` | Поднять PostgreSQL + приложение |
|
||||
| `make docker-down` | Остановить контейнеры |
|
||||
| `make docker-logs` | Логи приложения |
|
||||
@@ -75,6 +81,40 @@ make run
|
||||
| `make migrate-status` | Статус миграций |
|
||||
| `make migrate-create name=<name>` | Создать новую миграцию |
|
||||
|
||||
### Импорт данных (требует `SPOONACULAR_API_KEY`)
|
||||
|
||||
| Команда | Описание |
|
||||
|---|---|
|
||||
| `make import-ingredients` | Импортировать ~1 000 ингредиентов |
|
||||
| `make import-recipes` | Импортировать ~5 000 рецептов |
|
||||
| `make import-recipes-full` | Импортировать ~10 000 рецептов |
|
||||
|
||||
### Перевод (требует `GEMINI_API_KEY`)
|
||||
|
||||
| Команда | Описание |
|
||||
|---|---|
|
||||
| `make translate-recipes` | Перевести рецепты на русский |
|
||||
| `make translate-ingredients` | Перевести топ-200 ингредиентов |
|
||||
| `make import-all` | Полный пайплайн: ингредиенты → рецепты → переводы |
|
||||
|
||||
Все команды импорта идемпотентны (`ON CONFLICT DO UPDATE`) — можно запускать повторно. Для возобновления прерванного импорта используйте флаги `--skip-queries` / `--offset`.
|
||||
|
||||
### CLI напрямую
|
||||
|
||||
```bash
|
||||
# Тестовый прогон (без сохранения в БД)
|
||||
go run ./cmd/import import ingredients --limit 50 --dry-run
|
||||
go run ./cmd/import import recipes --count 100 --dry-run
|
||||
|
||||
# Возобновление импорта
|
||||
go run ./cmd/import import ingredients --limit 1000 --skip-queries 10
|
||||
go run ./cmd/import import recipes --count 5000 --offset 2000
|
||||
|
||||
# Перевод части рецептов
|
||||
go run ./cmd/import translate recipes --limit 1000
|
||||
go run ./cmd/import translate ingredients --top 50
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### Публичные эндпоинты
|
||||
@@ -120,18 +160,85 @@ curl -X PUT http://localhost:8080/profile \
|
||||
|
||||
```
|
||||
backend/
|
||||
├── cmd/server/ # Точка входа
|
||||
├── cmd/
|
||||
│ ├── server/ # HTTP-сервер (точка входа)
|
||||
│ └── import/ # CLI для импорта и перевода данных (Cobra)
|
||||
├── internal/
|
||||
│ ├── auth/ # Firebase-верификация, JWT, сервис и хэндлер авторизации
|
||||
│ ├── config/ # Конфигурация через переменные окружения
|
||||
│ ├── database/ # Подключение к PostgreSQL (pgxpool)
|
||||
│ ├── ingredient/ # Модель, репозиторий, сервис импорта ингредиентов
|
||||
│ ├── middleware/ # RequestID, Logging, Recovery, CORS, Auth
|
||||
│ ├── recipe/ # Модель, репозиторий, сервис импорта рецептов
|
||||
│ ├── server/ # Роутер (chi)
|
||||
│ ├── spoonacular/ # HTTP-клиент Spoonacular API (интерфейс + реализация)
|
||||
│ ├── testutil/ # Вспомогательные утилиты для тестов
|
||||
│ ├── translation/ # Gemini-переводчик, сервис батчевого перевода
|
||||
│ └── user/ # Модель, репозиторий, сервис, хэндлер, расчёт калорий
|
||||
├── migrations/ # SQL-миграции (goose)
|
||||
├── migrations/
|
||||
│ ├── 001_create_users.sql
|
||||
│ ├── 002_create_ingredient_mappings.sql # GIN-индекс по aliases
|
||||
│ └── 003_create_recipes.sql # FTS + GIN по ingredients/tags
|
||||
├── .env.example
|
||||
├── docker-compose.yml
|
||||
├── Dockerfile
|
||||
└── Makefile
|
||||
```
|
||||
|
||||
## Схема БД
|
||||
|
||||
### `ingredient_mappings`
|
||||
|
||||
Канонический справочник ингредиентов. Каждая запись — один вид продукта.
|
||||
|
||||
| Поле | Тип | Описание |
|
||||
|---|---|---|
|
||||
| `id` | UUID | Первичный ключ |
|
||||
| `canonical_name` | varchar | Нормализованное EN-название (`chicken_breast`) |
|
||||
| `canonical_name_ru` | varchar | Русское название (`куриная грудка`) |
|
||||
| `spoonacular_id` | integer | Уникальный ID из Spoonacular |
|
||||
| `aliases` | JSONB | Массив альтернативных названий (EN + RU) |
|
||||
| `category` | varchar | `produce`, `dairy`, `meat`, `seafood`, `grains`, `spices`, `canned`, `frozen`, `beverages`, `other` |
|
||||
| `default_unit` | varchar | Единица измерения по умолчанию (`g`, `ml`) |
|
||||
| `calories_per_100g` | decimal | Нутриенты на 100 г |
|
||||
| `storage_days` | integer | Типичный срок хранения (дни) |
|
||||
|
||||
Индексы: GIN по `aliases` (поиск `@>`), `canonical_name`, `category`, UNIQUE по `spoonacular_id`.
|
||||
|
||||
### `recipes`
|
||||
|
||||
Каталог рецептов. Заполняется из Spoonacular, переводится через Gemini.
|
||||
|
||||
| Поле | Тип | Описание |
|
||||
|---|---|---|
|
||||
| `id` | UUID | Первичный ключ |
|
||||
| `source` | enum | `spoonacular`, `ai`, `user` |
|
||||
| `spoonacular_id` | integer | Уникальный ID из Spoonacular |
|
||||
| `title` / `title_ru` | varchar | Название EN/RU |
|
||||
| `difficulty` | enum | `easy` (≤30 мин), `medium` (≤60 мин), `hard` |
|
||||
| `ingredients` | JSONB | Массив `{spoonacular_id, mapping_id, name, amount, unit}` |
|
||||
| `steps` | JSONB | Массив `{number, description, description_ru, timer_seconds}` |
|
||||
| `tags` | JSONB | `["vegetarian", "gluten-free", "meal:dinner", ...]` |
|
||||
| `calories_per_serving` | decimal | Нутриенты на порцию |
|
||||
| `avg_rating` / `review_count` | decimal/int | Рейтинг (обновляется при отзывах) |
|
||||
|
||||
Индексы: GIN по `ingredients` (поиск по `mapping_id`), GIN по `tags`, FTS по `title + title_ru`.
|
||||
|
||||
## Тесты
|
||||
|
||||
```bash
|
||||
# Unit-тесты (~69 тестов, ~13 сек)
|
||||
make test
|
||||
|
||||
# Интеграционные тесты (PostgreSQL в Docker через testcontainers)
|
||||
make test-integration
|
||||
```
|
||||
|
||||
| Пакет | Unit | Integration |
|
||||
|---|---|---|
|
||||
| `auth` | 17 | 10 |
|
||||
| `ingredient` | 9 | 5 |
|
||||
| `middleware` | 10 | — |
|
||||
| `recipe` | 12 | 7 |
|
||||
| `translation` | 6 | — |
|
||||
| `user` | 14 | 12 |
|
||||
|
||||
Reference in New Issue
Block a user