From 5b2cec5bcc851516f17d3beb9c4a81310e20abdd Mon Sep 17 00:00:00 2001 From: mirivlad Date: Mon, 1 Jun 2026 22:55:50 +0800 Subject: [PATCH] =?UTF-8?q?sync:=20fix=20SyncStatus=20binding=20=E2=80=94?= =?UTF-8?q?=20remove=20invalid=20type=20assertion,=20use=20config=20for=20?= =?UTF-8?q?device=20ID?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/verstak-gui/app.go | 88 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/cmd/verstak-gui/app.go b/cmd/verstak-gui/app.go index 4c306d6..d384e53 100644 --- a/cmd/verstak-gui/app.go +++ b/cmd/verstak-gui/app.go @@ -14,6 +14,7 @@ import ( "verstak/internal/core/actions" "verstak/internal/core/activity" + "verstak/internal/core/config" "verstak/internal/core/files" "verstak/internal/core/notes" "verstak/internal/core/nodes" @@ -835,6 +836,93 @@ func (a *App) Search(query string) ([]SearchResultDTO, error) { return out, nil } +// ============================================================ +// Sync +// ============================================================ + +type SyncStatusDTO struct { + Configured bool `json:"configured"` + ServerURL string `json:"serverUrl"` + DeviceID string `json:"deviceId"` + UnpushedOps int `json:"unpushedOps"` + LastSyncAt string `json:"lastSyncAt"` +} + +func (a *App) SyncStatus() (*SyncStatusDTO, error) { + serverURL, apiKey, _, lastSyncAt, err := a.sync.GetState() + if err != nil { + return &SyncStatusDTO{}, nil + } + unpushed, _ := a.sync.GetUnpushedOps() + cfg, _ := config.Load(a.vault) + dto := &SyncStatusDTO{ + Configured: serverURL != "" && apiKey != "", + ServerURL: serverURL, + UnpushedOps: len(unpushed), + LastSyncAt: lastSyncAt, + } + if cfg != nil { + dto.DeviceID = cfg.Sync.DeviceID + } + return dto, nil +} + +func (a *App) SyncConfigure(serverURL, apiKey string) error { + if err := a.sync.SetState(serverURL, apiKey); err != nil { + return err + } + // Persist to vault config. + cfg, err := config.Load(a.vault) + if err != nil { + return err + } + cfg.Sync.ServerURL = serverURL + cfg.Sync.APIKey = apiKey + return config.Save(a.vault, cfg) +} + +func (a *App) SyncNow() (map[string]interface{}, error) { + serverURL, apiKey, lastRev, _, err := a.sync.GetState() + if err != nil || serverURL == "" || apiKey == "" { + return nil, fmt.Errorf("sync not configured") + } + + deviceID := "" + if cfg, err := config.Load(a.vault); err == nil { + deviceID = cfg.Sync.DeviceID + } + + client := syncsvc.NewClient(serverURL, apiKey, deviceID, a.vault) + + // Push unpushed ops. + unpushed, err := a.sync.GetUnpushedOps() + if err != nil { + return nil, fmt.Errorf("get ops: %w", err) + } + pushResult := &syncsvc.PushResponse{} + if len(unpushed) > 0 { + pushResult, err = client.Push(unpushed) + if err != nil { + return nil, fmt.Errorf("push: %w", err) + } + if err := a.sync.MarkPushed(pushResult.Accepted); err != nil { + return nil, fmt.Errorf("mark pushed: %w", err) + } + } + + // Pull remote ops. + pullResult, err := client.Pull(lastRev) + if err != nil { + return nil, fmt.Errorf("pull: %w", err) + } + + return map[string]interface{}{ + "pushed": len(pushResult.Accepted), + "pulled": len(pullResult.Ops), + "serverRevision": pullResult.ServerRevision, + }, nil +} + // ============================================================ // File Dialogs (Wails v2 Runtime) // ============================================================