Backend (Go): - Project structure with chi router, pgxpool, goose migrations - JWT auth (access/refresh tokens) with Firebase token verification - NoopTokenVerifier for local dev without Firebase credentials - PostgreSQL user repository with atomic profile updates (transactions) - Mifflin-St Jeor calorie calculation based on profile data - REST API: POST /auth/login, /auth/refresh, /auth/logout, GET/PUT /profile, GET /health - Middleware: auth, CORS (localhost wildcard), logging, recovery, request_id - Unit tests (51 passing) and integration tests (testcontainers) - Docker Compose setup with postgres healthcheck and graceful shutdown Flutter client: - Riverpod state management with GoRouter navigation - Firebase Auth (email/password + Google sign-in with web popup support) - Platform-aware API URLs (web/Android/iOS) - Dio HTTP client with JWT auth interceptor and concurrent refresh handling - Secure token storage - Screens: Login, Register, Home (tabs: Menu, Recipes, Products, Profile) - Unit tests (17 passing) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
152 lines
6.0 KiB
Markdown
152 lines
6.0 KiB
Markdown
# FoodAI Client
|
||
|
||
Flutter-приложение для управления питанием с поддержкой iOS, Android и Web.
|
||
|
||
## Стек
|
||
|
||
- **Flutter 3 / Dart 3** — фреймворк
|
||
- **Riverpod** — управление состоянием
|
||
- **go_router** — навигация с auth guard
|
||
- **Dio** — HTTP-клиент с автоматическим обновлением токенов
|
||
- **Firebase Auth** — аутентификация (email + Google)
|
||
- **flutter_secure_storage** — безопасное хранение токенов
|
||
- **json_serializable** — генерация сериализации моделей
|
||
|
||
## Требования
|
||
|
||
- Flutter SDK 3.x (`flutter --version`)
|
||
- Dart SDK 3.9+
|
||
- Android Studio или Xcode (для запуска на симуляторе/устройстве)
|
||
- Chrome (для запуска в браузере)
|
||
- Проект Firebase с включёнными Email и Google Sign-In
|
||
|
||
## Быстрый старт
|
||
|
||
### 1. Установить зависимости
|
||
|
||
```bash
|
||
flutter pub get
|
||
```
|
||
|
||
### 2. Настроить Firebase
|
||
|
||
1. Создайте проект в [Firebase Console](https://console.firebase.google.com)
|
||
2. Включите **Authentication → Sign-in method**: Email/Password и Google
|
||
|
||
#### Android
|
||
|
||
1. Project Settings (⚙️) → **Your apps** → иконка Android (`🤖`)
|
||
2. Package name: `com.foodai.food_ai`
|
||
3. Скачать `google-services.json` → положить в `android/app/`:
|
||
```bash
|
||
cp ~/Downloads/google-services.json android/app/google-services.json
|
||
```
|
||
|
||
#### iOS
|
||
|
||
1. Project Settings (⚙️) → **Your apps** → иконка iOS (``)
|
||
2. Bundle ID: `com.foodai.foodAi`
|
||
3. Скачать `GoogleService-Info.plist` → добавить через Xcode в группу `Runner`:
|
||
```bash
|
||
open ios/Runner.xcworkspace
|
||
# Перетащить GoogleService-Info.plist в группу Runner в навигаторе Xcode
|
||
```
|
||
|
||
#### Web
|
||
|
||
1. Project Settings (⚙️) → **Your apps** → иконка Web (`</>`)
|
||
2. Nickname: `FoodAI Web`
|
||
3. Скопировать полученный `firebaseConfig` в `web/index.html`, заменив `YOUR_*` placeholder'ы
|
||
4. Убедиться, что `localhost` есть в списке авторизованных доменов:
|
||
Authentication → **Settings** → **Authorized domains**
|
||
|
||
> **Важно:** не коммитить конфиг-файлы в git:
|
||
> ```bash
|
||
> echo "android/app/google-services.json" >> .gitignore
|
||
> echo "ios/Runner/GoogleService-Info.plist" >> .gitignore
|
||
> ```
|
||
|
||
### 3. Настроить URL бэкенда
|
||
|
||
Откройте `lib/core/config/app_config.dart` и укажите адрес API:
|
||
|
||
```dart
|
||
static const development = AppConfig(
|
||
apiBaseUrl: 'http://10.0.2.2:9090', // Android-эмулятор → localhost:9090
|
||
// apiBaseUrl: 'http://localhost:9090', // iOS-симулятор / Web
|
||
);
|
||
|
||
static const production = AppConfig(
|
||
apiBaseUrl: 'https://api.food-ai.app',
|
||
);
|
||
```
|
||
|
||
> Бэкенд по умолчанию поднимается на порту `9090` через Docker Compose (`9090:8080`).
|
||
|
||
### 4. Сгенерировать код
|
||
|
||
```bash
|
||
flutter pub run build_runner build --delete-conflicting-outputs
|
||
```
|
||
|
||
### 5. Запустить приложение
|
||
|
||
```bash
|
||
flutter run
|
||
```
|
||
|
||
## Команды
|
||
|
||
| Команда | Описание |
|
||
|---|---|
|
||
| `flutter pub get` | Установить зависимости |
|
||
| `flutter run` | Запустить на подключённом устройстве/симуляторе |
|
||
| `flutter run -d chrome` | Запустить в браузере (web) |
|
||
| `flutter test` | Запустить все тесты |
|
||
| `flutter analyze` | Статический анализ кода |
|
||
| `flutter build apk` | Собрать APK для Android |
|
||
| `flutter build ios` | Собрать для iOS |
|
||
| `flutter pub run build_runner build` | Сгенерировать код (json_serializable) |
|
||
| `flutter pub run build_runner watch` | Следить за изменениями и генерировать |
|
||
|
||
## Структура проекта
|
||
|
||
```
|
||
client/
|
||
├── lib/
|
||
│ ├── core/
|
||
│ │ ├── api/ # ApiClient (Dio), AuthInterceptor, исключения
|
||
│ │ ├── auth/ # AuthService, AuthNotifier (Riverpod), SecureStorage
|
||
│ │ ├── config/ # AppConfig (URL бэкенда по окружению)
|
||
│ │ ├── router/ # GoRouter с auth guard и MainShell
|
||
│ │ └── theme/ # Цвета и тема Material 3
|
||
│ ├── features/
|
||
│ │ ├── auth/ # LoginScreen, RegisterScreen
|
||
│ │ ├── home/ # HomeScreen (placeholder)
|
||
│ │ ├── menu/ # MenuScreen (placeholder)
|
||
│ │ ├── products/ # ProductsScreen (placeholder)
|
||
│ │ ├── profile/ # ProfileScreen (placeholder)
|
||
│ │ └── recipes/ # RecipesScreen (placeholder)
|
||
│ ├── shared/
|
||
│ │ └── models/ # User (json_serializable + user.g.dart)
|
||
│ ├── app.dart # Корневой виджет
|
||
│ └── main.dart # Точка входа, инициализация Firebase
|
||
└── test/
|
||
├── features/auth/ # Тесты LoginScreen, RegisterScreen
|
||
└── shared/models/ # Тесты модели User
|
||
```
|
||
|
||
## Навигация
|
||
|
||
Приложение использует `go_router` с guard-ом авторизации:
|
||
|
||
- `/login` — экран входа (только для неавторизованных)
|
||
- `/register` — экран регистрации
|
||
- `/` → `/home` — главная (требует авторизации)
|
||
- `/products` — продукты
|
||
- `/menu` — меню дня
|
||
- `/recipes` — рецепты
|
||
- `/profile` — профиль пользователя
|
||
|
||
Неавторизованный пользователь автоматически перенаправляется на `/login`. После входа — на `/home`.
|