package main import ( "bytes" "context" "encoding/json" "fmt" "io" "mime/multipart" "net/http" "os" "strings" "syscall" "time" "git.t-juice.club/torjus/gpaste/api" "github.com/google/uuid" "github.com/urfave/cli/v2" "golang.org/x/term" ) var ( version = "dev" commit = "none" date = "unknown" ) func main() { cli.VersionFlag = &cli.BoolFlag{Name: "version"} app := cli.App{ Name: "gpaste", Version: fmt.Sprintf("gpaste %s-%s (%s)", version, commit, date), Flags: []cli.Flag{ &cli.StringFlag{ Name: "config", Usage: "Path to config-file.", }, &cli.StringFlag{ Name: "url", Usage: "Base url of gpaste server", }, }, Commands: []*cli.Command{ { Name: "upload", Usage: "Upload file(s)", ArgsUsage: "FILE [FILE]...", Action: ActionUpload, }, { Name: "login", Usage: "Login to gpaste server", ArgsUsage: "USERNAME", Action: ActionLogin, }, { Name: "admin", Usage: "Admin related commands", Subcommands: []*cli.Command{ { Name: "create-user", Usage: "Create a new user", ArgsUsage: "USERNAME", Action: ActionUserCreate, }, }, }, }, } app.Run(os.Args) } func ActionUpload(c *cli.Context) error { url := fmt.Sprintf("%s/api/file", c.String("url")) client := &http.Client{} // TODO: Change timeout ctx, cancel := context.WithTimeout(c.Context, 10*time.Minute) defer cancel() buf := &bytes.Buffer{} mw := multipart.NewWriter(buf) for _, arg := range c.Args().Slice() { f, err := os.Open(arg) if err != nil { return err } defer f.Close() fw, err := mw.CreateFormFile(uuid.Must(uuid.NewRandom()).String(), arg) if err != nil { return err } if _, err := io.Copy(fw, f); err != nil { return err } } mw.Close() req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, buf) if err != nil { return err } req.Header.Add("Content-Type", mw.FormDataContentType()) resp, err := client.Do(req) if err != nil { return err } defer resp.Body.Close() var expectedResp []struct { Message string `json:"message"` ID string `json:"id"` URL string `json:"url"` } decoder := json.NewDecoder(resp.Body) if err := decoder.Decode(&expectedResp); err != nil { return fmt.Errorf("error decoding response: %w", err) } for _, r := range expectedResp { fmt.Printf("Uploaded file %s\n", r.ID) } return nil } func ActionLogin(c *cli.Context) error { username := c.Args().First() if username == "" { return cli.Exit("USERNAME not supplied.", 1) } password, err := readPassword() if err != nil { return fmt.Errorf("error reading password: %w", err) } url := fmt.Sprintf("%s/api/login", c.String("url")) client := &http.Client{} // TODO: Change timeout ctx, cancel := context.WithTimeout(c.Context, 10*time.Second) defer cancel() body := new(bytes.Buffer) requestData := struct { Username string `json:"username"` Password string `json:"password"` }{ Username: username, Password: password, } encoder := json.NewEncoder(body) if err := encoder.Encode(&requestData); err != nil { return fmt.Errorf("error encoding response: %w", err) } req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, body) if err != nil { return fmt.Errorf("error creating request: %w", err) } resp, err := client.Do(req) if err != nil { return fmt.Errorf("unable to perform request: %s", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return cli.Exit("got non-ok response from server", 0) } responseData := struct { Token string `json:"token"` }{} decoder := json.NewDecoder(resp.Body) if err := decoder.Decode(&responseData); err != nil { return fmt.Errorf("unable to parse response: %s", err) } fmt.Printf("Token: %s", responseData.Token) return nil } func ActionUserCreate(c *cli.Context) error { // TODO: Needs to supply auth token to actually work username := c.Args().First() if username == "" { return cli.Exit("USERNAME not supplied.", 1) } password, err := readPassword() if err != nil { return fmt.Errorf("error reading password: %w", err) } url := fmt.Sprintf("%s/api/user", c.String("url")) client := &http.Client{} // TODO: Change timeout ctx, cancel := context.WithTimeout(c.Context, 10*time.Second) defer cancel() body := new(bytes.Buffer) requestData := &api.RequestAPIUserCreate{ Username: username, Password: password, } encoder := json.NewEncoder(body) if err := encoder.Encode(requestData); err != nil { return fmt.Errorf("error encoding response: %w", err) } req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, body) if err != nil { return fmt.Errorf("error creating request: %w", err) } resp, err := client.Do(req) if err != nil { return fmt.Errorf("unable to perform request: %s", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusAccepted { return cli.Exit("got non-ok response from server", 0) } fmt.Printf("Created user %s\n", username) return nil } func readPassword() (string, error) { fmt.Print("Enter Password: ") bytePassword, err := term.ReadPassword(int(syscall.Stdin)) if err != nil { return "", err } password := string(bytePassword) return strings.TrimSpace(password), nil }