cli: add sync push/pull/status commands
This commit is contained in:
parent
5b2cec5bcc
commit
1abe8c4fa0
|
|
@ -8,9 +8,11 @@ import (
|
|||
"strings"
|
||||
|
||||
"verstak/internal/core/actions"
|
||||
"verstak/internal/core/config"
|
||||
"verstak/internal/core/plugins"
|
||||
"verstak/internal/core/search"
|
||||
"verstak/internal/core/storage"
|
||||
syncsvc "verstak/internal/core/sync"
|
||||
"verstak/internal/core/vault"
|
||||
"verstak/internal/core/worklog"
|
||||
)
|
||||
|
|
@ -38,6 +40,8 @@ func main() {
|
|||
runLog(os.Args[2:])
|
||||
case "index":
|
||||
runIndex(os.Args[2:])
|
||||
case "sync":
|
||||
runSync(os.Args[2:])
|
||||
case "plugin":
|
||||
runPlugin(os.Args[2:])
|
||||
default:
|
||||
|
|
@ -56,6 +60,7 @@ func usage() {
|
|||
fmt.Println(" node Manage nodes")
|
||||
fmt.Println(" action Manage actions")
|
||||
fmt.Println(" --version Show version")
|
||||
fmt.Println(" sync Sync with server (push/pull/status)")
|
||||
fmt.Println(" --help Show this help")
|
||||
}
|
||||
|
||||
|
|
@ -597,6 +602,162 @@ func runIndexRebuild(args []string) {
|
|||
fmt.Printf("indexed %d nodes\n", count)
|
||||
}
|
||||
|
||||
// --- sync ---
|
||||
|
||||
func runSync(args []string) {
|
||||
if len(args) == 0 {
|
||||
fmt.Println("verstak sync — synchronize with server")
|
||||
fmt.Println()
|
||||
fmt.Println("Usage: verstak sync <command> [options]")
|
||||
fmt.Println()
|
||||
fmt.Println("Commands:")
|
||||
fmt.Println(" push Push local changes to server")
|
||||
fmt.Println(" pull Pull remote changes from server")
|
||||
fmt.Println(" status Show sync status")
|
||||
os.Exit(0)
|
||||
}
|
||||
switch args[0] {
|
||||
case "push":
|
||||
runSyncPush(args[1:])
|
||||
case "pull":
|
||||
runSyncPull(args[1:])
|
||||
case "status":
|
||||
runSyncStatus(args[1:])
|
||||
case "--help", "-h":
|
||||
runSync(nil)
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "Unknown sync command: %s\n", args[0])
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func openSyncDB(args []string) (*storage.DB, string) {
|
||||
vaultPath, _ := stringFlag(args, "--vault")
|
||||
abs, _ := filepath.Abs(vaultPath)
|
||||
dbPath := filepath.Join(abs, ".verstak", "index.db")
|
||||
db, err := storage.Open(dbPath)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Open vault: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
return db, abs
|
||||
}
|
||||
|
||||
func runSyncPush(args []string) {
|
||||
db, abs := openSyncDB(args)
|
||||
defer db.Close()
|
||||
|
||||
cfg, err := config.Load(abs)
|
||||
if err != nil || cfg.Sync.ServerURL == "" || cfg.Sync.APIKey == "" {
|
||||
fmt.Fprintln(os.Stderr, "Sync not configured. Use 'verstak sync configure' or GUI settings.")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
deviceID := cfg.Sync.DeviceID
|
||||
if deviceID == "" {
|
||||
deviceID = "cli-" + abs[:8]
|
||||
}
|
||||
|
||||
syncSvc := syncsvc.NewService(db, deviceID)
|
||||
client := syncsvc.NewClient(cfg.Sync.ServerURL, cfg.Sync.APIKey, deviceID, abs)
|
||||
|
||||
unpushed, err := syncSvc.GetUnpushedOps()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Get ops: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if len(unpushed) == 0 {
|
||||
fmt.Println("Nothing to push.")
|
||||
return
|
||||
}
|
||||
|
||||
result, err := client.Push(unpushed)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Push failed: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err := syncSvc.MarkPushed(result.Accepted); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Mark pushed: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("Pushed %d ops, accepted %d\n", len(unpushed), len(result.Accepted))
|
||||
}
|
||||
|
||||
func runSyncPull(args []string) {
|
||||
db, abs := openSyncDB(args)
|
||||
defer db.Close()
|
||||
|
||||
cfg, err := config.Load(abs)
|
||||
if err != nil || cfg.Sync.ServerURL == "" || cfg.Sync.APIKey == "" {
|
||||
fmt.Fprintln(os.Stderr, "Sync not configured.")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
deviceID := cfg.Sync.DeviceID
|
||||
if deviceID == "" {
|
||||
deviceID = "cli-" + abs[:8]
|
||||
}
|
||||
|
||||
syncSvc := syncsvc.NewService(db, deviceID)
|
||||
client := syncsvc.NewClient(cfg.Sync.ServerURL, cfg.Sync.APIKey, deviceID, abs)
|
||||
|
||||
_, _, lastRev, _, err := syncSvc.GetState()
|
||||
if err != nil {
|
||||
lastRev = 0
|
||||
}
|
||||
|
||||
result, err := client.Pull(lastRev)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Pull failed: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
var opIDs []string
|
||||
for _, op := range result.Ops {
|
||||
fmt.Printf(" %s\t%s\t%s\t%s\n", op.OpType, op.EntityType, op.EntityID, op.PayloadJSON)
|
||||
opIDs = append(opIDs, op.OpID)
|
||||
}
|
||||
|
||||
if len(opIDs) > 0 {
|
||||
syncSvc.MarkApplied(opIDs)
|
||||
}
|
||||
|
||||
fmt.Printf("Pulled %d ops (server rev: %d)\n", len(result.Ops), result.ServerRevision)
|
||||
}
|
||||
|
||||
func runSyncStatus(args []string) {
|
||||
db, abs := openSyncDB(args)
|
||||
defer db.Close()
|
||||
|
||||
cfg, err := config.Load(abs)
|
||||
configured := err == nil && cfg.Sync.ServerURL != "" && cfg.Sync.APIKey != ""
|
||||
serverURL := ""
|
||||
deviceID := ""
|
||||
if cfg != nil {
|
||||
serverURL = cfg.Sync.ServerURL
|
||||
deviceID = cfg.Sync.DeviceID
|
||||
}
|
||||
|
||||
unpushed := 0
|
||||
if configured {
|
||||
if deviceID == "" {
|
||||
deviceID = "cli-" + abs[:8]
|
||||
}
|
||||
syncSvc := syncsvc.NewService(db, deviceID)
|
||||
ops, _ := syncSvc.GetUnpushedOps()
|
||||
unpushed = len(ops)
|
||||
}
|
||||
|
||||
fmt.Println("Sync Status")
|
||||
fmt.Println(" Configured:", configured)
|
||||
fmt.Println(" Server:", serverURL)
|
||||
fmt.Println(" Device:", deviceID)
|
||||
fmt.Println(" Unpushed ops:", unpushed)
|
||||
}
|
||||
|
||||
// --- plugin ---
|
||||
|
||||
func runPlugin(args []string) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue