diff --git a/cmd/africastalking/main.go b/cmd/africastalking/main.go index 3f7e372..a1f4a0e 100644 --- a/cmd/africastalking/main.go +++ b/cmd/africastalking/main.go @@ -77,10 +77,10 @@ func main() { pfp := path.Join(scriptDir, "pp.csv") cfg := engine.Config{ - Root: "root", - OutputSize: uint32(size), - FlagCount: uint32(128), - MenuSeparator: menuSeparator, + Root: "root", + OutputSize: uint32(size), + FlagCount: uint32(128), + MenuSeparator: menuSeparator, ResetOnEmptyInput: true, } diff --git a/cmd/async/main.go b/cmd/async/main.go index 9a064a6..f131605 100644 --- a/cmd/async/main.go +++ b/cmd/async/main.go @@ -92,10 +92,10 @@ func main() { pfp := path.Join(scriptDir, "pp.csv") cfg := engine.Config{ - Root: "root", - OutputSize: uint32(size), - FlagCount: uint32(128), - MenuSeparator: menuSeparator, + Root: "root", + OutputSize: uint32(size), + FlagCount: uint32(128), + MenuSeparator: menuSeparator, ResetOnEmptyInput: true, } diff --git a/cmd/http/main.go b/cmd/http/main.go index 13ef408..303684d 100644 --- a/cmd/http/main.go +++ b/cmd/http/main.go @@ -78,10 +78,10 @@ func main() { pfp := path.Join(scriptDir, "pp.csv") cfg := engine.Config{ - Root: "root", - OutputSize: uint32(size), - FlagCount: uint32(128), - MenuSeparator: menuSeparator, + Root: "root", + OutputSize: uint32(size), + FlagCount: uint32(128), + MenuSeparator: menuSeparator, ResetOnEmptyInput: true, } diff --git a/cmd/ssh/main.go b/cmd/ssh/main.go index 96dd150..7588531 100644 --- a/cmd/ssh/main.go +++ b/cmd/ssh/main.go @@ -38,7 +38,7 @@ func main() { var stateDebug bool var host string var port uint - + flag.StringVar(&override.DbConn, "c", "?", "default connection string (replaces all unspecified strings)") flag.StringVar(&override.ResourceConn, "resource", "?", "resource connection string") flag.StringVar(&override.UserConn, "userdata", "?", "userdata store connection string") @@ -81,9 +81,9 @@ func main() { pfp := path.Join(scriptDir, "pp.csv") cfg := engine.Config{ - Root: "root", - OutputSize: uint32(size), - FlagCount: uint32(128), + Root: "root", + OutputSize: uint32(size), + FlagCount: uint32(128), ResetOnEmptyInput: true, } if stateDebug { diff --git a/config/config.go b/config/config.go index 863e418..cf33ffe 100644 --- a/config/config.go +++ b/config/config.go @@ -25,7 +25,7 @@ const ( defaultSSHHost string = "127.0.0.1" defaultSSHPort uint = 7122 defaultHTTPHost string = "127.0.0.1" - defaultHTTPPort uint = 7123 + defaultHTTPPort uint = 7123 defaultDomain = "sarafu.local" ) @@ -52,7 +52,6 @@ func SearchDomains() []string { return ParsedDomains } - func Language() string { return viseconfig.DefaultLanguage } diff --git a/devtools/admin/main.go b/devtools/admin/main.go index c3ce57f..4594efd 100644 --- a/devtools/admin/main.go +++ b/devtools/admin/main.go @@ -7,6 +7,7 @@ import ( "os" "path" + "git.defalsify.org/vise.git/engine" "git.defalsify.org/vise.git/logging" "git.grassecon.net/grassrootseconomics/sarafu-vise/config" @@ -16,8 +17,9 @@ import ( ) var ( - logg = logging.NewVanilla().WithContextKey("SessionId") - scriptDir = path.Join("services", "registration") + logg = logging.NewVanilla().WithContextKey("SessionId") + scriptDir = path.Join("services", "registration") + menuSeparator = ": " ) func main() { @@ -25,6 +27,8 @@ func main() { override := config.NewOverride() var sessionId string + var size uint + var engineDebug bool flag.StringVar(&sessionId, "session-id", "075xx2123", "session id") flag.StringVar(&override.DbConn, "c", "?", "default connection string (replaces all unspecified strings)") @@ -32,6 +36,8 @@ func main() { flag.StringVar(&override.UserConn, "userdata", "?", "userdata store connection string") flag.StringVar(&override.StateConn, "state", "?", "state store connection string") + flag.BoolVar(&engineDebug, "d", false, "use engine debug output") + flag.UintVar(&size, "s", 160, "max size of output") flag.Parse() config.Apply(override) @@ -50,13 +56,23 @@ func main() { os.Exit(1) } + cfg := engine.Config{ + Root: "root", + SessionId: sessionId, + OutputSize: uint32(size), + FlagCount: uint32(128), + MenuSeparator: menuSeparator, + EngineDebug: engineDebug, + ResetOnEmptyInput: true, + } + x := cmd.NewCmd(sessionId, flagParser) + x = x.WithEngine(cfg) err = x.Parse(flag.Args()) if err != nil { fmt.Fprintf(os.Stderr, "cmd parse fail: %v\n", err) os.Exit(1) } - logg.Infof("start command", "conn", conns, "subcmd", x) menuStorageService := storage.NewMenuStorageService(conns) @@ -70,5 +86,4 @@ func main() { fmt.Fprintf(os.Stderr, "cmd exec error: %v\n", err) os.Exit(1) } - } diff --git a/devtools/store/dump/main.go b/devtools/store/dump/main.go index 6844a22..338469b 100644 --- a/devtools/store/dump/main.go +++ b/devtools/store/dump/main.go @@ -24,7 +24,7 @@ func formatItem(k []byte, v []byte, sessionId string) (string, error) { if err != nil { return "", err } - s := fmt.Sprintf("%v\t%v\n", o.Label, string(v)) + s := fmt.Sprintf("%v\n\t%v\n", o.Label, string(v)) return s, nil } diff --git a/internal/cmd/cmd.go b/internal/cmd/cmd.go index ac286c1..255e270 100644 --- a/internal/cmd/cmd.go +++ b/internal/cmd/cmd.go @@ -3,23 +3,39 @@ package cmd import ( "context" "fmt" + "regexp" + "git.defalsify.org/vise.git/db" + "git.defalsify.org/vise.git/engine" "git.defalsify.org/vise.git/logging" + "git.defalsify.org/vise.git/persist" + "git.defalsify.org/vise.git/resource" + "git.defalsify.org/vise.git/state" "git.grassecon.net/grassrootseconomics/sarafu-vise/handlers/application" "git.grassecon.net/grassrootseconomics/visedriver/storage" ) +var argc map[string]int = map[string]int{ + "reset": 0, + "admin": 1, + "clone": 1, +} + var ( - logg = logging.NewVanilla().WithDomain("cmd").WithContextKey("SessionId") + logg = logging.NewVanilla().WithDomain("cmd").WithContextKey("SessionId") + cloneTargetRegex = `^\+000` ) type Cmd struct { - sessionId string - conn storage.ConnData - flagParser *application.FlagManager - cmd int - enable bool - exec func(ctx context.Context, ss storage.StorageService) error + sessionId string + conn storage.ConnData + flagParser *application.FlagManager + cmd int + enable bool + param string + exec func(ctx context.Context, ss storage.StorageService) error + engineConfig *engine.Config + st *state.State } func NewCmd(sessionId string, flagParser *application.FlagManager) *Cmd { @@ -29,10 +45,115 @@ func NewCmd(sessionId string, flagParser *application.FlagManager) *Cmd { } } +func (c *Cmd) WithEngine(engineConfig engine.Config) *Cmd { + c.engineConfig = &engineConfig + return c +} + func (c *Cmd) Exec(ctx context.Context, ss storage.StorageService) error { return c.exec(ctx, ss) } +func (c *Cmd) engine(ctx context.Context, rs resource.Resource, pe *persist.Persister) (engine.Engine, error) { + if c.engineConfig == nil { + return nil, fmt.Errorf("engine config missing") + } + en := engine.NewEngine(*c.engineConfig, rs) + + st := pe.GetState() + if st == nil { + return nil, fmt.Errorf("persister state fail") + } + en = en.WithState(st) + st.UseDebug() + ca := pe.GetMemory() + if ca == nil { + return nil, fmt.Errorf("persister cache fail") + } + en = en.WithMemory(ca) + logg.DebugCtxf(ctx, "state loaded", "state", st) + return en, nil +} + +func (c *Cmd) execClone(ctx context.Context, ss storage.StorageService) error { + re := regexp.MustCompile(cloneTargetRegex) + if !re.MatchString(c.param) { + return fmt.Errorf("Clone sessionId must match target: %s", c.param) + } + + pe, err := ss.GetPersister(ctx) + if err != nil { + return fmt.Errorf("get persister error: %v", err) + } + err = pe.Load(c.engineConfig.SessionId) + if err != nil { + return fmt.Errorf("persister load error: %v", err) + } + + /// TODO consider DRY with devtools/store/dump + store, err := ss.GetUserdataDb(ctx) + if err != nil { + return fmt.Errorf("store retrieve error: %v", err) + } + + store.SetSession(c.engineConfig.SessionId) + store.SetPrefix(db.DATATYPE_USERDATA) + dmp, err := store.Dump(ctx, []byte("")) + if err != nil { + return fmt.Errorf("store dump fail: %v\n", err.Error()) + } + + for true { + store.SetSession(c.engineConfig.SessionId) + k, v := dmp.Next(ctx) + if k == nil { + break + } + store.SetSession(c.param) + err = store.Put(ctx, k, v) + if err != nil { + return fmt.Errorf("user data store clone failed on key: %x", k) + } + } + + return pe.Save(c.param) +} + +func (c *Cmd) execReset(ctx context.Context, ss storage.StorageService) error { + pe, err := ss.GetPersister(ctx) + if err != nil { + return fmt.Errorf("get persister error: %v", err) + } + rs, err := ss.GetResource(ctx) + if err != nil { + return fmt.Errorf("get resource error: %v", err) + } + dbResource, ok := rs.(*resource.DbResource) + if !ok { + return fmt.Errorf("get dbresource error: %v", err) + } + err = pe.Load(c.engineConfig.SessionId) + if err != nil { + return fmt.Errorf("persister load error: %v", err) + } + en, err := c.engine(ctx, dbResource, pe) + if err != nil { + return err + } + _, err = en.(*engine.DefaultEngine).Reset(ctx, false) + if err != nil { + return err + } + st := pe.GetState() + logg.DebugCtxf(ctx, "state after reset", "state", st) + + err = pe.Save(c.engineConfig.SessionId) + if err != nil { + return err + } + return nil +} + func (c *Cmd) execAdmin(ctx context.Context, ss storage.StorageService) error { pe, err := ss.GetPersister(ctx) if err != nil { @@ -76,13 +197,43 @@ func (c *Cmd) parseCmdAdmin(cmd string, param string, more []string) (bool, erro return false, nil } +func (c *Cmd) parseCmdReset(cmd string, param string, more []string) (bool, error) { + if cmd == "reset" { + c.enable = false + c.exec = c.execReset + return true, nil + } + return false, nil +} + +func (c *Cmd) parseCmdClone(cmd string, param string, more []string) (bool, error) { + if cmd == "clone" { + c.enable = false + c.param = param + c.exec = c.execClone + return true, nil + } + return false, nil +} + func (c *Cmd) Parse(args []string) error { - if len(args) < 2 { + var param string + if len(args) < 1 { return fmt.Errorf("Wrong number of arguments: %v", args) } cmd := args[0] - param := args[1] - args = args[2:] + + n, ok := argc[cmd] + if !ok { + return fmt.Errorf("invalid command: %v", cmd) + } + if n > 0 { + if len(args) < n+1 { + return fmt.Errorf("Wrong number of arguments, need: %d", n) + } + param = args[1] + args = args[2:] + } r, err := c.parseCmdAdmin(cmd, param, args) if err != nil { @@ -92,5 +243,21 @@ func (c *Cmd) Parse(args []string) error { return nil } + r, err = c.parseCmdReset(cmd, param, args) + if err != nil { + return err + } + if r { + return nil + } + + r, err = c.parseCmdClone(cmd, param, args) + if err != nil { + return err + } + if r { + return nil + } + return fmt.Errorf("unknown subcommand: %s", cmd) } diff --git a/profile/profile_test.go b/profile/profile_test.go index a210646..e8fe1b0 100644 --- a/profile/profile_test.go +++ b/profile/profile_test.go @@ -10,9 +10,9 @@ import ( func TestInsertOrShift(t *testing.T) { tests := []struct { name string - profile Profile - index int - value string + profile Profile + index int + value string expected []string }{ { diff --git a/ssh/ssh.go b/ssh/ssh.go index bab538a..f6d1dbb 100644 --- a/ssh/ssh.go +++ b/ssh/ssh.go @@ -181,8 +181,8 @@ func (s *SshRunner) GetEngine(sessionId string) (engine.Engine, func(), error) { accountService := services.New(ctx, menuStorageService) _, err = lhs.GetHandler(accountService) if err != nil { - fmt.Fprintf(os.Stderr, "get accounts service handler: %v\n", err) - os.Exit(1) + fmt.Fprintf(os.Stderr, "get accounts service handler: %v\n", err) + os.Exit(1) } en := lhs.GetEngine(lhs.Cfg, rs, pe) closer := func() {