# Local-First Architecture Integration Guide ## Overview QuoteForge теперь поддерживает local-first архитектуру: приложение ВСЕГДА работает с SQLite (localdb), MariaDB используется только для синхронизации. ## Реализованные компоненты ### 1. Конвертеры моделей (`internal/localdb/converters.go`) Конвертеры между MariaDB и SQLite моделями: - `ConfigurationToLocal()` / `LocalToConfiguration()` - `PricelistToLocal()` / `LocalToPricelist()` - `ComponentToLocal()` / `LocalToComponent()` ### 2. LocalDB методы (`internal/localdb/localdb.go`) Добавлены методы для работы с pending changes: - `MarkChangesSynced(ids []int64)` - помечает изменения как синхронизированные - `GetPendingCount()` - возвращает количество несинхронизированных изменений ### 3. Sync Service расширения (`internal/services/sync/service.go`) Новые методы: - `SyncPricelistsIfNeeded()` - проверяет и скачивает новые прайслисты при необходимости - `PushPendingChanges()` - отправляет все pending changes на сервер - `pushSingleChange()` - обрабатывает один pending change - `pushConfigurationCreate/Update/Delete()` - специфичные методы для конфигураций **ВАЖНО**: Конструктор изменен - теперь требует `ConfigurationRepository`: ```go syncService := sync.NewService(pricelistRepo, configRepo, local) ``` ### 4. LocalConfigurationService (`internal/services/local_configuration.go`) Новый сервис для работы с конфигурациями в local-first режиме: - Все операции CRUD работают через SQLite - Автоматически добавляет изменения в pending_changes - При создании конфигурации (если online) проверяет новые прайслисты ```go localConfigService := services.NewLocalConfigurationService( localDB, syncService, quoteService, isOnlineFunc, ) ``` ### 5. Sync Handler расширения (`internal/handlers/sync.go`) Новые endpoints: - `POST /api/sync/push` - отправить pending changes на сервер - `GET /api/sync/pending/count` - получить количество pending changes - `GET /api/sync/pending` - получить список pending changes ## Интеграция ### Шаг 1: Обновить main.go ```go // В cmd/qfs/main.go syncService := sync.NewService(pricelistRepo, configRepo, local) // Создать isOnline функцию isOnlineFunc := func() bool { sqlDB, err := db.DB() if err != nil { return false } return sqlDB.Ping() == nil } // Создать LocalConfigurationService localConfigService := services.NewLocalConfigurationService( local, syncService, quoteService, isOnlineFunc, ) ``` ### Шаг 2: Обновить ConfigurationHandler Заменить `ConfigurationService` на `LocalConfigurationService` в handlers: ```go // Было: configHandler := handlers.NewConfigurationHandler(configService, exportService) // Стало: configHandler := handlers.NewConfigurationHandler(localConfigService, exportService) ``` ### Шаг 3: Добавить endpoints для sync В роутере добавить: ```go syncGroup := router.Group("/api/sync") { syncGroup.POST("/push", syncHandler.PushPendingChanges) syncGroup.GET("/pending/count", syncHandler.GetPendingCount) syncGroup.GET("/pending", syncHandler.GetPendingChanges) } ``` ## Как это работает ### Создание конфигурации 1. Пользователь создает конфигурацию 2. `LocalConfigurationService.Create()`: - Если online → `SyncPricelistsIfNeeded()` проверяет новые прайслисты - Сохраняет конфигурацию в SQLite - Добавляет в `pending_changes` с operation="create" 3. Конфигурация доступна локально сразу ### Синхронизация с сервером **Manual sync:** ```bash POST /api/sync/push ``` **Background sync (TODO):** - Периодический worker вызывает `syncService.PushPendingChanges()` - Проверяет online статус - Отправляет все pending changes на сервер - Удаляет успешно синхронизированные записи ### Offline режим 1. Все операции работают нормально через SQLite 2. Изменения копятся в `pending_changes` 3. При восстановлении соединения автоматически синхронизируются ## Pending Changes Queue Таблица `pending_changes`: ```go type PendingChange struct { ID int64 // Auto-increment EntityType string // "configuration", "project", "specification" EntityUUID string // UUID сущности Operation string // "create", "update", "delete" Payload string // JSON snapshot сущности CreatedAt time.Time Attempts int // Счетчик попыток синхронизации LastError string // Последняя ошибка синхронизации } ``` ## TODO для Phase 2.5 - [ ] Background sync worker (автоматическая синхронизация каждые N минут) - [ ] Conflict resolution (при конфликтах обновления) - [ ] UI: pending counter в header - [ ] UI: manual sync button - [ ] UI: conflict alerts - [ ] Retry logic для failed pending changes - [ ] RefreshPrices для local mode (через local_components) ## Testing ```bash # Compile go build ./cmd/qfs # Run ./quoteforge # Check pending changes curl http://localhost:8080/api/sync/pending/count # Manual sync curl -X POST http://localhost:8080/api/sync/push ```