♻️ Refactor synchronization service
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -29,7 +29,7 @@ type ServiceManager struct {
|
||||
badgeService *dock.DockService
|
||||
notificationService *notifications.NotificationService
|
||||
testService *TestService // 测试服务(仅开发环境)
|
||||
BackupService *BackupService
|
||||
SyncService *SyncService
|
||||
httpClientService *HttpClientService // HTTP客户端服务
|
||||
logger *log.LogService
|
||||
}
|
||||
@@ -95,8 +95,8 @@ func NewServiceManager() *ServiceManager {
|
||||
// 初始化主题服务
|
||||
themeService := NewThemeService(databaseService, logger)
|
||||
|
||||
// 初始化备份服务
|
||||
backupService := NewBackupService(configService, databaseService, logger)
|
||||
// 初始化同步服务
|
||||
syncService := NewSyncService(configService, databaseService, logger)
|
||||
|
||||
// 初始化HTTP客户端服务
|
||||
httpClientService := NewHttpClientService(logger)
|
||||
@@ -124,7 +124,7 @@ func NewServiceManager() *ServiceManager {
|
||||
badgeService: badgeService,
|
||||
notificationService: notificationService,
|
||||
testService: testService,
|
||||
BackupService: backupService,
|
||||
SyncService: syncService,
|
||||
httpClientService: httpClientService,
|
||||
logger: logger,
|
||||
}
|
||||
@@ -150,7 +150,7 @@ func (sm *ServiceManager) GetServices() []application.Service {
|
||||
application.NewService(sm.badgeService),
|
||||
application.NewService(sm.notificationService),
|
||||
application.NewService(sm.testService),
|
||||
application.NewService(sm.BackupService),
|
||||
application.NewService(sm.SyncService),
|
||||
application.NewService(sm.httpClientService),
|
||||
}
|
||||
return services
|
||||
|
||||
242
internal/services/sync_service.go
Normal file
242
internal/services/sync_service.go
Normal file
@@ -0,0 +1,242 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"time"
|
||||
"voidraft/internal/common/helper"
|
||||
"voidraft/internal/models"
|
||||
"voidraft/internal/syncer"
|
||||
|
||||
"github.com/wailsapp/wails/v3/pkg/application"
|
||||
"github.com/wailsapp/wails/v3/pkg/services/log"
|
||||
)
|
||||
|
||||
const (
|
||||
syncDir = "sync"
|
||||
localFSHeadKey = "head.json"
|
||||
)
|
||||
|
||||
// SyncService 提供应用层同步服务入口。
|
||||
type SyncService struct {
|
||||
configService *ConfigService
|
||||
dbService *DatabaseService
|
||||
logger *log.LogService
|
||||
app *syncer.App
|
||||
cancelObservers []helper.CancelFunc
|
||||
}
|
||||
|
||||
// NewSyncService 创建新的同步服务实例。
|
||||
func NewSyncService(configService *ConfigService, dbService *DatabaseService, logger *log.LogService) *SyncService {
|
||||
return &SyncService{
|
||||
configService: configService,
|
||||
dbService: dbService,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// ServiceStartup 在服务启动时初始化同步系统。
|
||||
func (s *SyncService) ServiceStartup(ctx context.Context, options application.ServiceOptions) error {
|
||||
_ = options
|
||||
|
||||
if err := s.ensureApp(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.cancelObservers = []helper.CancelFunc{
|
||||
s.configService.Watch("sync", s.onSyncConfigChange),
|
||||
s.configService.Watch("general.dataPath", s.onDataPathChange),
|
||||
}
|
||||
|
||||
if err := s.Initialize(); err != nil {
|
||||
s.logger.Error("initializing sync service: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Initialize 重新加载配置并启动自动同步。
|
||||
func (s *SyncService) Initialize() error {
|
||||
if err := s.ensureApp(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
config, err := s.buildConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.app.Reconfigure(context.Background(), config); err != nil {
|
||||
return fmt.Errorf("reconfigure sync app: %w", err)
|
||||
}
|
||||
if err := s.app.Start(context.Background()); err != nil {
|
||||
return fmt.Errorf("start sync app: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Reinitialize 重新初始化同步服务。
|
||||
func (s *SyncService) Reinitialize() error {
|
||||
return s.Initialize()
|
||||
}
|
||||
|
||||
// HandleConfigChange 在配置变化时重新应用配置。
|
||||
func (s *SyncService) HandleConfigChange(config *models.SyncConfig) error {
|
||||
_ = config
|
||||
return s.Initialize()
|
||||
}
|
||||
|
||||
// StartAutoSync 启动自动同步调度。
|
||||
func (s *SyncService) StartAutoSync() error {
|
||||
if err := s.ensureApp(); err != nil {
|
||||
return err
|
||||
}
|
||||
return s.app.Start(context.Background())
|
||||
}
|
||||
|
||||
// StopAutoSync 停止自动同步调度。
|
||||
func (s *SyncService) StopAutoSync() {
|
||||
if s.app == nil {
|
||||
return
|
||||
}
|
||||
if err := s.app.Stop(context.Background()); err != nil {
|
||||
s.logger.Warning("stop sync app: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Sync 执行一次手动同步。
|
||||
func (s *SyncService) Sync() error {
|
||||
if err := s.ensureApp(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
targetID, err := s.selectedTargetID()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := s.app.Sync(context.Background(), targetID); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ServiceShutdown 停止同步服务并释放资源。
|
||||
func (s *SyncService) ServiceShutdown() {
|
||||
for _, cancel := range s.cancelObservers {
|
||||
if cancel != nil {
|
||||
cancel()
|
||||
}
|
||||
}
|
||||
s.StopAutoSync()
|
||||
}
|
||||
|
||||
// onSyncConfigChange 响应 sync 配置变化。
|
||||
func (s *SyncService) onSyncConfigChange(oldValue interface{}, newValue interface{}) {
|
||||
_, _ = oldValue, newValue
|
||||
if err := s.Initialize(); err != nil {
|
||||
s.logger.Error("reconfigure sync after sync config change: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// onDataPathChange 响应数据目录变化。
|
||||
func (s *SyncService) onDataPathChange(oldValue interface{}, newValue interface{}) {
|
||||
_, _ = oldValue, newValue
|
||||
if err := s.Reinitialize(); err != nil {
|
||||
s.logger.Error("reconfigure sync after data path change: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// ensureApp 保证同步应用已被创建。
|
||||
func (s *SyncService) ensureApp() error {
|
||||
if s.app != nil {
|
||||
return nil
|
||||
}
|
||||
if s.dbService == nil || s.dbService.Client == nil {
|
||||
return fmt.Errorf("sync database client is not ready")
|
||||
}
|
||||
|
||||
s.app = syncer.NewApp(s.dbService.Client, syncer.Options{
|
||||
Logger: s.logger,
|
||||
MaxSyncAttempts: 3,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
// buildConfig 将现有应用配置映射为同步核心配置。
|
||||
func (s *SyncService) buildConfig() (syncer.Config, error) {
|
||||
appConfig, err := s.configService.GetConfig()
|
||||
if err != nil {
|
||||
return syncer.Config{}, err
|
||||
}
|
||||
|
||||
return syncer.Config{
|
||||
Targets: []syncer.TargetConfig{
|
||||
s.buildGitTargetConfig(appConfig.General.DataPath, appConfig.Sync.Git),
|
||||
s.buildLocalFSTargetConfig(appConfig.Sync.LocalFS),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// selectedTargetID 返回当前选中的同步目标标识。
|
||||
func (s *SyncService) selectedTargetID() (string, error) {
|
||||
appConfig, err := s.configService.GetConfig()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
switch appConfig.Sync.Target {
|
||||
case models.SyncTargetGit:
|
||||
return string(models.SyncTargetGit), nil
|
||||
case models.SyncTargetLocalFS:
|
||||
return string(models.SyncTargetLocalFS), nil
|
||||
default:
|
||||
return "", fmt.Errorf("unsupported sync target: %s", appConfig.Sync.Target)
|
||||
}
|
||||
}
|
||||
|
||||
// buildGitTargetConfig 将 Git 配置转换为同步核心目标配置。
|
||||
func (s *SyncService) buildGitTargetConfig(dataPath string, config models.GitSyncConfig) syncer.TargetConfig {
|
||||
return syncer.TargetConfig{
|
||||
Kind: syncer.TargetKindGit,
|
||||
Enabled: config.Enabled,
|
||||
Schedule: syncer.ScheduleConfig{
|
||||
AutoSync: config.AutoSync,
|
||||
Interval: time.Duration(config.SyncInterval) * time.Minute,
|
||||
},
|
||||
Git: &syncer.GitTargetConfig{
|
||||
RepoPath: filepath.Join(dataPath, syncDir),
|
||||
RepoURL: config.RepoURL,
|
||||
Branch: syncer.DefaultBranch,
|
||||
RemoteName: syncer.DefaultRemoteName,
|
||||
AuthorName: "voidraft",
|
||||
AuthorEmail: "sync@voidraft.app",
|
||||
Auth: syncer.GitAuthConfig{
|
||||
Method: string(config.AuthMethod),
|
||||
Username: config.Username,
|
||||
Password: config.Password,
|
||||
Token: config.Token,
|
||||
SSHKeyPath: config.SSHKeyPath,
|
||||
SSHKeyPassword: config.SSHKeyPass,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// buildLocalFSTargetConfig 将 localfs 配置转换为同步核心目标配置。
|
||||
func (s *SyncService) buildLocalFSTargetConfig(config models.LocalFSSyncConfig) syncer.TargetConfig {
|
||||
return syncer.TargetConfig{
|
||||
Kind: syncer.TargetKindLocalFS,
|
||||
Enabled: config.Enabled,
|
||||
Schedule: syncer.ScheduleConfig{
|
||||
AutoSync: config.AutoSync,
|
||||
Interval: time.Duration(config.SyncInterval) * time.Minute,
|
||||
},
|
||||
LocalFS: &syncer.LocalFSTargetConfig{
|
||||
Namespace: string(models.SyncTargetLocalFS),
|
||||
HeadKey: localFSHeadKey,
|
||||
RootPath: config.RootPath,
|
||||
},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user