refactor: migrate sync service and handlers to use ConnectionManager
Updated sync-related code to use ConnectionManager instead of direct database references: - SyncService now creates repositories on-demand when connection available - SyncHandler uses ConnectionManager for lazy DB access - Added ComponentFilter and ListComponents to localdb for offline queries - All sync operations check connection status before attempting MariaDB access This completes the transition to offline-first architecture where all database access goes through ConnectionManager. Part of Phase 2.5: Full Offline Mode Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -8,21 +8,21 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"git.mchus.pro/mchus/quoteforge/internal/db"
|
||||||
"git.mchus.pro/mchus/quoteforge/internal/localdb"
|
"git.mchus.pro/mchus/quoteforge/internal/localdb"
|
||||||
"git.mchus.pro/mchus/quoteforge/internal/services/sync"
|
"git.mchus.pro/mchus/quoteforge/internal/services/sync"
|
||||||
"gorm.io/gorm"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// SyncHandler handles sync API endpoints
|
// SyncHandler handles sync API endpoints
|
||||||
type SyncHandler struct {
|
type SyncHandler struct {
|
||||||
localDB *localdb.LocalDB
|
localDB *localdb.LocalDB
|
||||||
syncService *sync.Service
|
syncService *sync.Service
|
||||||
mariaDB *gorm.DB
|
connMgr *db.ConnectionManager
|
||||||
tmpl *template.Template
|
tmpl *template.Template
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSyncHandler creates a new sync handler
|
// NewSyncHandler creates a new sync handler
|
||||||
func NewSyncHandler(localDB *localdb.LocalDB, syncService *sync.Service, mariaDB *gorm.DB, templatesPath string) (*SyncHandler, error) {
|
func NewSyncHandler(localDB *localdb.LocalDB, syncService *sync.Service, connMgr *db.ConnectionManager, templatesPath string) (*SyncHandler, error) {
|
||||||
// Load sync_status partial template
|
// Load sync_status partial template
|
||||||
partialPath := filepath.Join(templatesPath, "partials", "sync_status.html")
|
partialPath := filepath.Join(templatesPath, "partials", "sync_status.html")
|
||||||
tmpl, err := template.ParseFiles(partialPath)
|
tmpl, err := template.ParseFiles(partialPath)
|
||||||
@@ -33,7 +33,7 @@ func NewSyncHandler(localDB *localdb.LocalDB, syncService *sync.Service, mariaDB
|
|||||||
return &SyncHandler{
|
return &SyncHandler{
|
||||||
localDB: localDB,
|
localDB: localDB,
|
||||||
syncService: syncService,
|
syncService: syncService,
|
||||||
mariaDB: mariaDB,
|
connMgr: connMgr,
|
||||||
tmpl: tmpl,
|
tmpl: tmpl,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
@@ -109,7 +109,17 @@ func (h *SyncHandler) SyncComponents(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := h.localDB.SyncComponents(h.mariaDB)
|
// Get database connection from ConnectionManager
|
||||||
|
mariaDB, err := h.connMgr.GetDB()
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusServiceUnavailable, gin.H{
|
||||||
|
"success": false,
|
||||||
|
"error": "Database connection failed: " + err.Error(),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := h.localDB.SyncComponents(mariaDB)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("component sync failed", "error", err)
|
slog.Error("component sync failed", "error", err)
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{
|
c.JSON(http.StatusInternalServerError, gin.H{
|
||||||
@@ -181,7 +191,16 @@ func (h *SyncHandler) SyncAll(c *gin.Context) {
|
|||||||
var componentsSynced, pricelistsSynced int
|
var componentsSynced, pricelistsSynced int
|
||||||
|
|
||||||
// Sync components
|
// Sync components
|
||||||
compResult, err := h.localDB.SyncComponents(h.mariaDB)
|
mariaDB, err := h.connMgr.GetDB()
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusServiceUnavailable, gin.H{
|
||||||
|
"success": false,
|
||||||
|
"error": "Database connection failed: " + err.Error(),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
compResult, err := h.localDB.SyncComponents(mariaDB)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("component sync failed during full sync", "error", err)
|
slog.Error("component sync failed during full sync", "error", err)
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{
|
c.JSON(http.StatusInternalServerError, gin.H{
|
||||||
@@ -215,16 +234,7 @@ func (h *SyncHandler) SyncAll(c *gin.Context) {
|
|||||||
|
|
||||||
// checkOnline checks if MariaDB is accessible
|
// checkOnline checks if MariaDB is accessible
|
||||||
func (h *SyncHandler) checkOnline() bool {
|
func (h *SyncHandler) checkOnline() bool {
|
||||||
sqlDB, err := h.mariaDB.DB()
|
return h.connMgr.IsOnline()
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := sqlDB.Ping(); err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PushPendingChanges pushes all pending changes to the server
|
// PushPendingChanges pushes all pending changes to the server
|
||||||
|
|||||||
@@ -9,6 +9,13 @@ import (
|
|||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ComponentFilter for searching with filters
|
||||||
|
type ComponentFilter struct {
|
||||||
|
Category string
|
||||||
|
Search string
|
||||||
|
HasPrice bool
|
||||||
|
}
|
||||||
|
|
||||||
// ComponentSyncResult contains statistics from component sync
|
// ComponentSyncResult contains statistics from component sync
|
||||||
type ComponentSyncResult struct {
|
type ComponentSyncResult struct {
|
||||||
TotalSynced int
|
TotalSynced int
|
||||||
@@ -196,6 +203,44 @@ func (l *LocalDB) SearchLocalComponentsByCategory(category string, query string,
|
|||||||
return components, err
|
return components, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ListComponents returns components with filtering and pagination
|
||||||
|
func (l *LocalDB) ListComponents(filter ComponentFilter, offset, limit int) ([]LocalComponent, int64, error) {
|
||||||
|
db := l.db
|
||||||
|
|
||||||
|
// Apply category filter
|
||||||
|
if filter.Category != "" {
|
||||||
|
db = db.Where("LOWER(category) = ?", strings.ToLower(filter.Category))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply search filter
|
||||||
|
if filter.Search != "" {
|
||||||
|
searchPattern := "%" + strings.ToLower(filter.Search) + "%"
|
||||||
|
db = db.Where(
|
||||||
|
"LOWER(lot_name) LIKE ? OR LOWER(lot_description) LIKE ? OR LOWER(category) LIKE ? OR LOWER(model) LIKE ?",
|
||||||
|
searchPattern, searchPattern, searchPattern, searchPattern,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply price filter
|
||||||
|
if filter.HasPrice {
|
||||||
|
db = db.Where("current_price IS NOT NULL")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get total count
|
||||||
|
var total int64
|
||||||
|
if err := db.Model(&LocalComponent{}).Count(&total).Error; err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply pagination and get results
|
||||||
|
var components []LocalComponent
|
||||||
|
if err := db.Order("lot_name").Offset(offset).Limit(limit).Find(&components).Error; err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return components, total, nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetLocalComponent returns a single component by lot_name
|
// GetLocalComponent returns a single component by lot_name
|
||||||
func (l *LocalDB) GetLocalComponent(lotName string) (*LocalComponent, error) {
|
func (l *LocalDB) GetLocalComponent(lotName string) (*LocalComponent, error) {
|
||||||
var component LocalComponent
|
var component LocalComponent
|
||||||
@@ -266,3 +311,100 @@ func (l *LocalDB) NeedComponentSync(maxAgeHours int) bool {
|
|||||||
}
|
}
|
||||||
return time.Since(*syncTime).Hours() > float64(maxAgeHours)
|
return time.Since(*syncTime).Hours() > float64(maxAgeHours)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateComponentPricesFromPricelist updates current_price in local_components from pricelist items
|
||||||
|
// This allows offline price updates using synced pricelists without MariaDB connection
|
||||||
|
func (l *LocalDB) UpdateComponentPricesFromPricelist(pricelistID uint) (int, error) {
|
||||||
|
// Get all items from the specified pricelist
|
||||||
|
var items []LocalPricelistItem
|
||||||
|
if err := l.db.Where("pricelist_id = ?", pricelistID).Find(&items).Error; err != nil {
|
||||||
|
return 0, fmt.Errorf("fetching pricelist items: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(items) == 0 {
|
||||||
|
slog.Warn("no items found in pricelist", "pricelist_id", pricelistID)
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update current_price for each component
|
||||||
|
updated := 0
|
||||||
|
err := l.db.Transaction(func(tx *gorm.DB) error {
|
||||||
|
for _, item := range items {
|
||||||
|
result := tx.Model(&LocalComponent{}).
|
||||||
|
Where("lot_name = ?", item.LotName).
|
||||||
|
Update("current_price", item.Price)
|
||||||
|
|
||||||
|
if result.Error != nil {
|
||||||
|
return fmt.Errorf("updating price for %s: %w", item.LotName, result.Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
if result.RowsAffected > 0 {
|
||||||
|
updated++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
slog.Info("updated component prices from pricelist",
|
||||||
|
"pricelist_id", pricelistID,
|
||||||
|
"total_items", len(items),
|
||||||
|
"updated_components", updated)
|
||||||
|
|
||||||
|
return updated, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnsureComponentPricesFromPricelists loads prices from the latest pricelist into local_components
|
||||||
|
// if no components exist or all current prices are NULL
|
||||||
|
func (l *LocalDB) EnsureComponentPricesFromPricelists() error {
|
||||||
|
// Check if we have any components with prices
|
||||||
|
var count int64
|
||||||
|
if err := l.db.Model(&LocalComponent{}).Where("current_price IS NOT NULL").Count(&count).Error; err != nil {
|
||||||
|
return fmt.Errorf("checking component prices: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have components with prices, don't load from pricelists
|
||||||
|
if count > 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we have any components at all
|
||||||
|
var totalComponents int64
|
||||||
|
if err := l.db.Model(&LocalComponent{}).Count(&totalComponents).Error; err != nil {
|
||||||
|
return fmt.Errorf("counting components: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have no components, we need to load them from pricelists
|
||||||
|
if totalComponents == 0 {
|
||||||
|
slog.Info("no components found in local database, loading from latest pricelist")
|
||||||
|
// This would typically be called from the sync service or setup process
|
||||||
|
// For now, we'll just return nil to indicate no action needed
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have components but no prices, we should load prices from pricelists
|
||||||
|
// Find the latest pricelist
|
||||||
|
var latestPricelist LocalPricelist
|
||||||
|
if err := l.db.Order("created_at DESC").First(&latestPricelist).Error; err != nil {
|
||||||
|
if err == gorm.ErrRecordNotFound {
|
||||||
|
slog.Warn("no pricelists found in local database")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("finding latest pricelist: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update prices from the latest pricelist
|
||||||
|
updated, err := l.UpdateComponentPricesFromPricelist(latestPricelist.ID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("updating component prices from pricelist: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
slog.Info("loaded component prices from latest pricelist",
|
||||||
|
"pricelist_id", latestPricelist.ID,
|
||||||
|
"updated_components", updated)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"log/slog"
|
"log/slog"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"git.mchus.pro/mchus/quoteforge/internal/db"
|
||||||
"git.mchus.pro/mchus/quoteforge/internal/localdb"
|
"git.mchus.pro/mchus/quoteforge/internal/localdb"
|
||||||
"git.mchus.pro/mchus/quoteforge/internal/models"
|
"git.mchus.pro/mchus/quoteforge/internal/models"
|
||||||
"git.mchus.pro/mchus/quoteforge/internal/repository"
|
"git.mchus.pro/mchus/quoteforge/internal/repository"
|
||||||
@@ -13,17 +14,15 @@ import (
|
|||||||
|
|
||||||
// Service handles synchronization between MariaDB and local SQLite
|
// Service handles synchronization between MariaDB and local SQLite
|
||||||
type Service struct {
|
type Service struct {
|
||||||
pricelistRepo *repository.PricelistRepository
|
connMgr *db.ConnectionManager
|
||||||
configRepo *repository.ConfigurationRepository
|
localDB *localdb.LocalDB
|
||||||
localDB *localdb.LocalDB
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewService creates a new sync service
|
// NewService creates a new sync service
|
||||||
func NewService(pricelistRepo *repository.PricelistRepository, configRepo *repository.ConfigurationRepository, localDB *localdb.LocalDB) *Service {
|
func NewService(connMgr *db.ConnectionManager, localDB *localdb.LocalDB) *Service {
|
||||||
return &Service{
|
return &Service{
|
||||||
pricelistRepo: pricelistRepo,
|
connMgr: connMgr,
|
||||||
configRepo: configRepo,
|
localDB: localDB,
|
||||||
localDB: localDB,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,10 +38,14 @@ type SyncStatus struct {
|
|||||||
func (s *Service) GetStatus() (*SyncStatus, error) {
|
func (s *Service) GetStatus() (*SyncStatus, error) {
|
||||||
lastSync := s.localDB.GetLastSyncTime()
|
lastSync := s.localDB.GetLastSyncTime()
|
||||||
|
|
||||||
// Count server pricelists
|
// Count server pricelists (requires connection)
|
||||||
serverPricelists, _, err := s.pricelistRepo.List(0, 1)
|
serverCount := 0
|
||||||
if err != nil {
|
if mariaDB, err := s.connMgr.GetDB(); err == nil && mariaDB != nil {
|
||||||
return nil, fmt.Errorf("counting server pricelists: %w", err)
|
pricelistRepo := repository.NewPricelistRepository(mariaDB)
|
||||||
|
serverPricelists, _, err := pricelistRepo.List(0, 1)
|
||||||
|
if err == nil {
|
||||||
|
serverCount = len(serverPricelists)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Count local pricelists
|
// Count local pricelists
|
||||||
@@ -52,7 +55,7 @@ func (s *Service) GetStatus() (*SyncStatus, error) {
|
|||||||
|
|
||||||
return &SyncStatus{
|
return &SyncStatus{
|
||||||
LastSyncAt: lastSync,
|
LastSyncAt: lastSync,
|
||||||
ServerPricelists: len(serverPricelists),
|
ServerPricelists: serverCount,
|
||||||
LocalPricelists: int(localCount),
|
LocalPricelists: int(localCount),
|
||||||
NeedsSync: needsSync,
|
NeedsSync: needsSync,
|
||||||
}, nil
|
}, nil
|
||||||
@@ -73,8 +76,15 @@ func (s *Service) NeedSync() (bool, error) {
|
|||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if there are new pricelists on server
|
// Check if there are new pricelists on server (requires connection)
|
||||||
latestServer, err := s.pricelistRepo.GetLatestActive()
|
mariaDB, err := s.connMgr.GetDB()
|
||||||
|
if err != nil {
|
||||||
|
// If offline, can't check server, no need to sync
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
pricelistRepo := repository.NewPricelistRepository(mariaDB)
|
||||||
|
latestServer, err := pricelistRepo.GetLatestActive()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// If no pricelists on server, no need to sync
|
// If no pricelists on server, no need to sync
|
||||||
return false, nil
|
return false, nil
|
||||||
@@ -98,18 +108,29 @@ func (s *Service) NeedSync() (bool, error) {
|
|||||||
func (s *Service) SyncPricelists() (int, error) {
|
func (s *Service) SyncPricelists() (int, error) {
|
||||||
slog.Info("starting pricelist sync")
|
slog.Info("starting pricelist sync")
|
||||||
|
|
||||||
|
// Get database connection
|
||||||
|
mariaDB, err := s.connMgr.GetDB()
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("database not available: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create repository
|
||||||
|
pricelistRepo := repository.NewPricelistRepository(mariaDB)
|
||||||
|
|
||||||
// Get all active pricelists from server (up to 100)
|
// Get all active pricelists from server (up to 100)
|
||||||
serverPricelists, _, err := s.pricelistRepo.List(0, 100)
|
serverPricelists, _, err := pricelistRepo.List(0, 100)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, fmt.Errorf("getting server pricelists: %w", err)
|
return 0, fmt.Errorf("getting server pricelists: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
synced := 0
|
synced := 0
|
||||||
|
var latestLocalID uint
|
||||||
for _, pl := range serverPricelists {
|
for _, pl := range serverPricelists {
|
||||||
// Check if pricelist already exists locally
|
// Check if pricelist already exists locally
|
||||||
existing, _ := s.localDB.GetLocalPricelistByServerID(pl.ID)
|
existing, _ := s.localDB.GetLocalPricelistByServerID(pl.ID)
|
||||||
if existing != nil {
|
if existing != nil {
|
||||||
// Already synced, skip
|
// Already synced, track latest
|
||||||
|
latestLocalID = existing.ID
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,8 +149,27 @@ func (s *Service) SyncPricelists() (int, error) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sync items for the newly created pricelist
|
||||||
|
itemCount, err := s.SyncPricelistItems(localPL.ID)
|
||||||
|
if err != nil {
|
||||||
|
slog.Warn("failed to sync pricelist items", "version", pl.Version, "error", err)
|
||||||
|
// Continue even if items sync fails - we have the pricelist metadata
|
||||||
|
} else {
|
||||||
|
slog.Debug("synced pricelist with items", "version", pl.Version, "items", itemCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
latestLocalID = localPL.ID
|
||||||
synced++
|
synced++
|
||||||
slog.Debug("synced pricelist", "version", pl.Version, "server_id", pl.ID)
|
}
|
||||||
|
|
||||||
|
// Update component prices from latest pricelist
|
||||||
|
if latestLocalID > 0 {
|
||||||
|
updated, err := s.localDB.UpdateComponentPricesFromPricelist(latestLocalID)
|
||||||
|
if err != nil {
|
||||||
|
slog.Warn("failed to update component prices from pricelist", "error", err)
|
||||||
|
} else {
|
||||||
|
slog.Info("updated component prices from latest pricelist", "updated", updated)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update last sync time
|
// Update last sync time
|
||||||
@@ -154,8 +194,17 @@ func (s *Service) SyncPricelistItems(localPricelistID uint) (int, error) {
|
|||||||
return int(existingCount), nil
|
return int(existingCount), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get database connection
|
||||||
|
mariaDB, err := s.connMgr.GetDB()
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("database not available: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create repository
|
||||||
|
pricelistRepo := repository.NewPricelistRepository(mariaDB)
|
||||||
|
|
||||||
// Get items from server
|
// Get items from server
|
||||||
serverItems, _, err := s.pricelistRepo.GetItems(localPL.ServerID, 0, 10000, "")
|
serverItems, _, err := pricelistRepo.GetItems(localPL.ServerID, 0, 10000, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, fmt.Errorf("getting server pricelist items: %w", err)
|
return 0, fmt.Errorf("getting server pricelist items: %w", err)
|
||||||
}
|
}
|
||||||
@@ -312,8 +361,17 @@ func (s *Service) pushConfigurationCreate(change *localdb.PendingChange) error {
|
|||||||
return fmt.Errorf("unmarshaling configuration: %w", err)
|
return fmt.Errorf("unmarshaling configuration: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get database connection
|
||||||
|
mariaDB, err := s.connMgr.GetDB()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("database not available: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create repository
|
||||||
|
configRepo := repository.NewConfigurationRepository(mariaDB)
|
||||||
|
|
||||||
// Create on server
|
// Create on server
|
||||||
if err := s.configRepo.Create(&cfg); err != nil {
|
if err := configRepo.Create(&cfg); err != nil {
|
||||||
return fmt.Errorf("creating configuration on server: %w", err)
|
return fmt.Errorf("creating configuration on server: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -337,6 +395,15 @@ func (s *Service) pushConfigurationUpdate(change *localdb.PendingChange) error {
|
|||||||
return fmt.Errorf("unmarshaling configuration: %w", err)
|
return fmt.Errorf("unmarshaling configuration: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get database connection
|
||||||
|
mariaDB, err := s.connMgr.GetDB()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("database not available: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create repository
|
||||||
|
configRepo := repository.NewConfigurationRepository(mariaDB)
|
||||||
|
|
||||||
// Ensure we have a server ID before updating
|
// Ensure we have a server ID before updating
|
||||||
// If the payload doesn't have ID, get it from local configuration
|
// If the payload doesn't have ID, get it from local configuration
|
||||||
if cfg.ID == 0 {
|
if cfg.ID == 0 {
|
||||||
@@ -347,7 +414,7 @@ func (s *Service) pushConfigurationUpdate(change *localdb.PendingChange) error {
|
|||||||
|
|
||||||
if localCfg.ServerID == nil {
|
if localCfg.ServerID == nil {
|
||||||
// Configuration hasn't been synced yet, try to find it on server by UUID
|
// Configuration hasn't been synced yet, try to find it on server by UUID
|
||||||
serverCfg, err := s.configRepo.GetByUUID(cfg.UUID)
|
serverCfg, err := configRepo.GetByUUID(cfg.UUID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("configuration not yet synced to server: %w", err)
|
return fmt.Errorf("configuration not yet synced to server: %w", err)
|
||||||
}
|
}
|
||||||
@@ -363,7 +430,7 @@ func (s *Service) pushConfigurationUpdate(change *localdb.PendingChange) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update on server
|
// Update on server
|
||||||
if err := s.configRepo.Update(&cfg); err != nil {
|
if err := configRepo.Update(&cfg); err != nil {
|
||||||
return fmt.Errorf("updating configuration on server: %w", err)
|
return fmt.Errorf("updating configuration on server: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -380,8 +447,17 @@ func (s *Service) pushConfigurationUpdate(change *localdb.PendingChange) error {
|
|||||||
|
|
||||||
// pushConfigurationDelete deletes a configuration from the server
|
// pushConfigurationDelete deletes a configuration from the server
|
||||||
func (s *Service) pushConfigurationDelete(change *localdb.PendingChange) error {
|
func (s *Service) pushConfigurationDelete(change *localdb.PendingChange) error {
|
||||||
|
// Get database connection
|
||||||
|
mariaDB, err := s.connMgr.GetDB()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("database not available: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create repository
|
||||||
|
configRepo := repository.NewConfigurationRepository(mariaDB)
|
||||||
|
|
||||||
// Get the configuration from server by UUID to get the ID
|
// Get the configuration from server by UUID to get the ID
|
||||||
cfg, err := s.configRepo.GetByUUID(change.EntityUUID)
|
cfg, err := configRepo.GetByUUID(change.EntityUUID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Already deleted or not found, consider it successful
|
// Already deleted or not found, consider it successful
|
||||||
slog.Warn("configuration not found on server, considering delete successful", "uuid", change.EntityUUID)
|
slog.Warn("configuration not found on server, considering delete successful", "uuid", change.EntityUUID)
|
||||||
@@ -389,7 +465,7 @@ func (s *Service) pushConfigurationDelete(change *localdb.PendingChange) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Delete from server
|
// Delete from server
|
||||||
if err := s.configRepo.Delete(cfg.ID); err != nil {
|
if err := configRepo.Delete(cfg.ID); err != nil {
|
||||||
return fmt.Errorf("deleting configuration from server: %w", err)
|
return fmt.Errorf("deleting configuration from server: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user