monorepo/native/desktop/maplefile/internal/service/sync/service.go

149 lines
4.4 KiB
Go

package sync
import (
"context"
"fmt"
"go.uber.org/zap"
"codeberg.org/mapleopentech/monorepo/native/desktop/maplefile/internal/domain/syncstate"
)
// Service provides unified sync operations
type Service interface {
// SyncAll synchronizes both collections and files
SyncAll(ctx context.Context, input *SyncInput) (*SyncResult, error)
// SyncCollections synchronizes collections only
SyncCollections(ctx context.Context, input *SyncInput) (*SyncResult, error)
// SyncFiles synchronizes files only
SyncFiles(ctx context.Context, input *SyncInput) (*SyncResult, error)
// GetSyncStatus returns the current sync status
GetSyncStatus(ctx context.Context) (*SyncStatus, error)
// ResetSync resets all sync state for a fresh sync
ResetSync(ctx context.Context) error
}
type service struct {
logger *zap.Logger
collectionSync CollectionSyncService
fileSync FileSyncService
repoProvider RepositoryProvider
}
// ProvideService creates a new unified sync service for Wire
func ProvideService(
logger *zap.Logger,
collectionSync CollectionSyncService,
fileSync FileSyncService,
repoProvider RepositoryProvider,
) Service {
return &service{
logger: logger.Named("SyncService"),
collectionSync: collectionSync,
fileSync: fileSync,
repoProvider: repoProvider,
}
}
// getSyncStateRepo returns the sync state repository, or an error if not initialized
func (s *service) getSyncStateRepo() (syncstate.Repository, error) {
if !s.repoProvider.IsInitialized() {
return nil, fmt.Errorf("storage not initialized - user must be logged in")
}
repo := s.repoProvider.GetSyncStateRepository()
if repo == nil {
return nil, fmt.Errorf("sync state repository not available")
}
return repo, nil
}
// SyncAll synchronizes both collections and files
func (s *service) SyncAll(ctx context.Context, input *SyncInput) (*SyncResult, error) {
s.logger.Info("Starting full sync (collections + files)")
// Sync collections first
colResult, err := s.collectionSync.Execute(ctx, input)
if err != nil {
s.logger.Error("Collection sync failed during full sync", zap.Error(err))
return nil, err
}
// Sync files
fileResult, err := s.fileSync.Execute(ctx, input)
if err != nil {
s.logger.Error("File sync failed during full sync", zap.Error(err))
// Return partial result with collection data
return &SyncResult{
CollectionsProcessed: colResult.CollectionsProcessed,
CollectionsAdded: colResult.CollectionsAdded,
CollectionsUpdated: colResult.CollectionsUpdated,
CollectionsDeleted: colResult.CollectionsDeleted,
Errors: append(colResult.Errors, "file sync failed: "+err.Error()),
}, err
}
// Merge results
result := &SyncResult{
CollectionsProcessed: colResult.CollectionsProcessed,
CollectionsAdded: colResult.CollectionsAdded,
CollectionsUpdated: colResult.CollectionsUpdated,
CollectionsDeleted: colResult.CollectionsDeleted,
FilesProcessed: fileResult.FilesProcessed,
FilesAdded: fileResult.FilesAdded,
FilesUpdated: fileResult.FilesUpdated,
FilesDeleted: fileResult.FilesDeleted,
Errors: append(colResult.Errors, fileResult.Errors...),
}
s.logger.Info("Full sync completed",
zap.Int("collections_processed", result.CollectionsProcessed),
zap.Int("files_processed", result.FilesProcessed),
zap.Int("errors", len(result.Errors)))
return result, nil
}
// SyncCollections synchronizes collections only
func (s *service) SyncCollections(ctx context.Context, input *SyncInput) (*SyncResult, error) {
return s.collectionSync.Execute(ctx, input)
}
// SyncFiles synchronizes files only
func (s *service) SyncFiles(ctx context.Context, input *SyncInput) (*SyncResult, error) {
return s.fileSync.Execute(ctx, input)
}
// GetSyncStatus returns the current sync status
func (s *service) GetSyncStatus(ctx context.Context) (*SyncStatus, error) {
syncStateRepo, err := s.getSyncStateRepo()
if err != nil {
return nil, err
}
state, err := syncStateRepo.Get()
if err != nil {
return nil, err
}
return &SyncStatus{
CollectionsSynced: state.IsCollectionSyncComplete(),
FilesSynced: state.IsFileSyncComplete(),
FullySynced: state.IsFullySynced(),
}, nil
}
// ResetSync resets all sync state for a fresh sync
func (s *service) ResetSync(ctx context.Context) error {
s.logger.Info("Resetting sync state")
syncStateRepo, err := s.getSyncStateRepo()
if err != nil {
return err
}
return syncStateRepo.Reset()
}