diff --git a/cmd/cli/commands/list/list.go b/cmd/cli/commands/list/list.go new file mode 100644 index 0000000..1619502 --- /dev/null +++ b/cmd/cli/commands/list/list.go @@ -0,0 +1,61 @@ +package list + +import ( + "context" + "flag" + "fmt" + "mirror-sync/cmd/cli/config" + "mirror-sync/pkg/client" + "mirror-sync/pkg/project" + "os" + + "github.com/google/subcommands" +) + +type ( + ListCmd struct { + projectName string + } +) + +func (*ListCmd) Name() string { return "list" } +func (*ListCmd) Synopsis() string { return "list the scheduled projects" } +func (*ListCmd) Usage() string { + return `Usage: mirror-sync list [--project-name] + +list the scheduled projects + +Options: +` +} + +func (p *ListCmd) SetFlags(f *flag.FlagSet) { + f.StringVar(&p.projectName, "project-name", "", "show only one project") +} + +func (p *ListCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus { + clientConfig := config.Load() + + cli := client.New(clientConfig.Deamon.URL) + + prs, err := cli.List() + if err != nil { + fmt.Fprintf(os.Stderr, "error: %s\n", err) + return subcommands.ExitFailure + } + + for _, pr := range prs { + print(pr) + } + + return subcommands.ExitSuccess +} + +func print(pr project.Project) { + fmt.Println(pr.Name) + fmt.Println("------------------") + + for _, repo := range pr.Repositories { + fmt.Printf("%-20s | %s -> %s | %s\n", repo.Name, repo.Source, repo.Destination, repo.Schedule) + } +} diff --git a/cmd/cli/config/config.go b/cmd/cli/config/config.go new file mode 100644 index 0000000..03d4c4b --- /dev/null +++ b/cmd/cli/config/config.go @@ -0,0 +1,52 @@ +package config + +import ( + "encoding/json" + "errors" + "os" + "path/filepath" +) + +type ( + ClientConfiguration struct { + Deamon ClientDaemonConfiguration `json:"daemon"` + } + + ClientDaemonConfiguration struct { + URL string `json:"url"` + } +) + +func Load() ClientConfiguration { + userConfigDir, err := os.UserConfigDir() + if err != nil { + panic("failed to get user config path: " + err.Error()) + } + + path := filepath.Join(userConfigDir, "mirror-sync", "config.json") + + f, err := os.OpenFile(path, os.O_RDONLY, 0) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + return Default() + } + panic("failed to load configuration file (" + path + "): " + err.Error()) + } + defer f.Close() + + var c ClientConfiguration + d := json.NewDecoder(f) + if err := d.Decode(&c); err != nil { + panic("failed to read the configuration file: " + err.Error()) + } + + return c +} + +func Default() ClientConfiguration { + return ClientConfiguration{ + Deamon: ClientDaemonConfiguration{ + URL: "http://localhost:8080", + }, + } +} diff --git a/cmd/cli/main.go b/cmd/cli/main.go index 28cdddc..db4d309 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -3,7 +3,9 @@ package main import ( "context" "flag" + "fmt" "mirror-sync/cmd/cli/commands/apply" + "mirror-sync/cmd/cli/commands/list" "mirror-sync/cmd/cli/commands/version" "os" @@ -11,6 +13,12 @@ import ( ) func main() { + defer func() { + if r := recover(); r != nil { + fmt.Fprintln(os.Stderr, "fatal:", r) + } + }() + subcommands.Register(subcommands.HelpCommand(), "help") subcommands.Register(subcommands.FlagsCommand(), "help") subcommands.Register(subcommands.CommandsCommand(), "help") @@ -18,6 +26,8 @@ func main() { subcommands.Register(&apply.ApplyCmd{}, "projects") + subcommands.Register(&list.ListCmd{}, "management") + flag.Parse() ctx := context.Background() diff --git a/cmd/server/core/storage/storage.go b/cmd/server/core/storage/storage.go index c5291ac..b8414b8 100644 --- a/cmd/server/core/storage/storage.go +++ b/cmd/server/core/storage/storage.go @@ -233,7 +233,7 @@ func (r *Repository) List() ([]project.Project, error) { } defer rows.Close() - stmt, err := r.db.Prepare("SELECT name, schedule, source, destination WHERE uuid = ?") + stmt, err := r.db.Prepare("SELECT name, schedule, source, destination FROM Repositories WHERE uuid = ?") if err != nil { return nil, fmt.Errorf("invalid syntax: %w", err) } diff --git a/pkg/client/client.go b/pkg/client/client.go index fbfc629..efed382 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -59,6 +59,36 @@ func (c *Client) Apply(pr project.Project) error { return nil } +func (c *Client) List() ([]project.Project, error) { + url, err := url.JoinPath(c.url, "api", "v1", "projects", "all") + if err != nil { + return nil, fmt.Errorf("failed to make url: %s", err) + } + + res, err := http.Get(url) + if err != nil { + return nil, fmt.Errorf("failed to send the request to the server: %s", err) + } + defer res.Body.Close() + + if res.StatusCode != 201 { + return nil, fmt.Errorf("failed to send the request to the server: %s: %s", res.Status, toError(res.Body)) + } + + var prs []project.Project + d := json.NewDecoder(res.Body) + if err := d.Decode(&prs); err != nil { + return nil, fmt.Errorf("failed to parse the server response, is the client you up-to-date? (reason: %s)", err) + } + + for i, pr := range prs { + pr.ServerURL = c.url + prs[i] = pr + } + + return prs, nil +} + func toError(body io.ReadCloser) error { var msg SimpleError