diff --git a/cmd/photos-api-cli/error.go b/cmd/photos-api-cli/error.go new file mode 100644 index 0000000..c01190d --- /dev/null +++ b/cmd/photos-api-cli/error.go @@ -0,0 +1,18 @@ +package main + +import ( + "fmt" + "strings" +) + +type ResponseError struct { + Err string `json:"error"` + Details []string `json:"details"` +} + +func (u *ResponseError) Error() string { + if len(u.Details) == 0 { + return u.Err + } + return fmt.Sprintf("%s: \n - %s", u.Err, strings.Join(u.Details, "\n - ")) +} diff --git a/cmd/photos-api-cli/login.go b/cmd/photos-api-cli/login.go index c029b7e..ae6ecae 100644 --- a/cmd/photos-api-cli/login.go +++ b/cmd/photos-api-cli/login.go @@ -1,7 +1,6 @@ package main import ( - "errors" "fmt" "github.com/go-resty/resty/v2" @@ -18,10 +17,6 @@ type LoginRequest struct { Password string `json:"password"` } -type LoginError struct { - Error string -} - type LoginResponse struct { Token string } @@ -35,16 +30,16 @@ func (c *LoginCommand) Execute(args []string) error { R(). SetBody(&LoginRequest{c.Login, c.Password}). SetResult(&LoginResponse{}). - SetError(&LoginError{}). + SetError(&ResponseError{}). Post("/account/login") if err != nil { return err } - if err, ok := resp.Error().(*LoginError); ok { + if resp.IsError() { logger.Printf("Login failed!") - return errors.New(err.Error) + return resp.Error().(*ResponseError) } logger.Println("Login succeed!") diff --git a/cmd/photos-api-cli/register.go b/cmd/photos-api-cli/register.go index 5438730..1a8bafe 100644 --- a/cmd/photos-api-cli/register.go +++ b/cmd/photos-api-cli/register.go @@ -1,8 +1,6 @@ package main import ( - "errors" - "github.com/go-resty/resty/v2" ) @@ -17,13 +15,6 @@ type RegisterRequest struct { Password string `json:"password"` } -type RegisterResponse struct { -} - -type RegisterError struct { - Error string -} - func (c *RegisterCommand) Execute(args []string) error { logger.Printf("Registering on %s...\n", c.Url) @@ -31,18 +22,17 @@ func (c *RegisterCommand) Execute(args []string) error { resp, err := cli. R(). + SetError(&ResponseError{}). SetBody(&RegisterRequest{c.Login, c.Password}). - SetResult(&RegisterResponse{}). - SetError(&RegisterError{}). Post("/account/signup") if err != nil { return err } - if err, ok := resp.Error().(*RegisterError); ok { + if resp.IsError() { logger.Println("Registering failed!") - return errors.New(err.Error) + return resp.Error().(*ResponseError) } logger.Println("Registering succeed!") diff --git a/cmd/photos-api-cli/upload.go b/cmd/photos-api-cli/upload.go index c40d913..0b66cfe 100644 --- a/cmd/photos-api-cli/upload.go +++ b/cmd/photos-api-cli/upload.go @@ -1,16 +1,12 @@ package main import ( - "crypto/sha1" "crypto/sha256" "encoding/hex" - "errors" "fmt" "io" "os" "path/filepath" - "strings" - "sync" "github.com/go-resty/resty/v2" "github.com/schollz/progressbar/v3" @@ -24,18 +20,6 @@ type UploadCommand struct { Workers uint32 `short:"w" long:"workers" description:"Number of workers for uploading chunks" default:"4"` } -type UploadError struct { - Err string `json:"error"` - Details []string `json:"details"` -} - -func (u *UploadError) Error() string { - if len(u.Details) == 0 { - return u.Err - } - return fmt.Sprintf("%s: \n - %s", u.Err, strings.Join(u.Details, "\n - ")) -} - type UploadCreate struct { UploadId string `json:"upload_id"` } @@ -47,171 +31,19 @@ type UploadPartResult struct { PartSha256 string `json:"sha256"` } -type UploadCompleteRequest struct { - Sha256 string `json:"sha256" binding:"required,sha256"` - Name string `json:"name" binding:"required"` - Parts uint `json:"parts" binding:"required"` -} - -type UploadFileRequest struct { - Name string - Checksum string - Chunks []string -} - -type UploadFileResponse struct { - Sum string - NbChunks uint32 - Size uint64 -} - func (c *UploadCommand) Cli() *resty.Client { return resty.New().SetBaseURL(c.Url).SetAuthScheme("Private").SetAuthToken(c.Token) } -func (c *UploadCommand) FileExists() (string, error) { - f, err := os.Open(c.File) - if err != nil { - return "", err - } - defer f.Close() - - st, err := f.Stat() - if err != nil { - return "", err - } - - progress := progressbar.DefaultBytes(st.Size(), fmt.Sprintf("Checking %s", filepath.Base(c.File))) - defer progress.Close() - tee := io.TeeReader(f, progress) - checksum := sha1.New() - io.Copy(checksum, tee) - sum := hex.EncodeToString(checksum.Sum(nil)) - - resp, err := c.Cli().R().Head(fmt.Sprintf("/file/%s", sum)) - if err != nil { - return "", err - } - if resp.IsSuccess() { - return "", errors.New("file already exists") - } - return sum, nil -} - -func (c *UploadCommand) FileUpload(sum string) error { - f, err := os.Open(c.File) - if err != nil { - return err - } - defer f.Close() - - st, err := f.Stat() - if err != nil { - return err - } - nbChunks := st.Size() / photosapi.CHUNK_SIZE - if st.Size()%photosapi.CHUNK_SIZE > 0 { - nbChunks++ - } - - uploadFile := &UploadFileRequest{ - Name: filepath.Base(c.File), - Chunks: make([]string, nbChunks), - Checksum: sum, - } - - cli := c.Cli() - progress := progressbar.DefaultBytes(st.Size(), fmt.Sprintf("Uploading %s", uploadFile.Name)) - defer progress.Close() - - wg := sync.WaitGroup{} - mu := sync.Mutex{} - wg.Add(4) - wgErrors := make([]error, c.Workers) - i := int64(0) - for w := uint32(0); w < c.Workers; w++ { - go func(w uint32) { - defer wg.Done() - b := make([]byte, photosapi.CHUNK_SIZE) - for { - mu.Lock() - part := i - i++ - n, err := f.Read(b) - mu.Unlock() - if n == 0 { - if err == io.EOF { - break - } - wgErrors[w] = err - return - } - - checksum := sha1.New() - checksum.Write(b[0:n]) - sum := hex.EncodeToString(checksum.Sum(nil)) - - resp, err := cli.R().Head(fmt.Sprintf("/file/chunk/%s", sum)) - if err != nil { - wgErrors[w] = err - return - } - - if resp.IsSuccess() { - uploadFile.Chunks[part] = sum - progress.Add(n) - continue - } - - resp, err = cli.R().SetError(&UploadError{}).SetBody(b[0:n]).Post("/file/chunk") - if err != nil { - wgErrors[w] = err - return - } - - if err, ok := resp.Error().(*UploadError); ok { - wgErrors[w] = err - return - } - - uploadFile.Chunks[part] = sum - progress.Add(n) - } - }(w) - } - wg.Wait() - for _, err := range wgErrors { - if err != nil { - return err - } - } - - resp, err := cli.R().SetBody(uploadFile).SetError(&UploadError{}).SetResult(&UploadFileResponse{}).Post("/file") - if err != nil { - return err - } - - if err, ok := resp.Error().(*UploadError); ok { - logger.Println("Upload failed") - return err - } - - if result, ok := resp.Result().(*UploadFileResponse); ok { - fmt.Printf("Upload succeed\nSum: %s\nNbChunks: %d\nSize: %d\n", result.Sum, result.NbChunks, result.Size) - } - - return nil -} - func (c *UploadCommand) Execute(args []string) error { cli := c.Cli() - resp, err := cli.R().SetError(&UploadError{}).SetResult(&UploadCreate{}).Post("/upload") + resp, err := cli.R().SetError(&ResponseError{}).SetResult(&UploadCreate{}).Post("/upload") if err != nil { return err } - if err, ok := resp.Error().(*UploadError); ok { - return err + if resp.IsError() { + return resp.Error().(*ResponseError) } uploadId := resp.Result().(*UploadCreate).UploadId @@ -249,7 +81,7 @@ func (c *UploadCommand) Execute(args []string) error { resp, err := cli. R(). - SetError(&UploadError{}). + SetError(&ResponseError{}). SetResult(&UploadPartResult{}). SetQueryParam("part", fmt.Sprint(parts)). SetQueryParam("sha256", hex.EncodeToString(partsha256.Sum(nil))). @@ -261,8 +93,8 @@ func (c *UploadCommand) Execute(args []string) error { return err } - if err, ok := resp.Error().(*UploadError); ok { - return err + if resp.IsError() { + return resp.Error().(*ResponseError) } } @@ -274,9 +106,9 @@ func (c *UploadCommand) Execute(args []string) error { resp, err = cli. R(). - SetError(&UploadError{}). + SetError(&ResponseError{}). SetPathParam("id", uploadId). - SetBody(&UploadCompleteRequest{ + SetBody(&photosapi.UploadCompleteRequest{ Sha256: hex.EncodeToString(completesha256.Sum(nil)), Parts: uint(parts), Name: filepath.Base(c.File), @@ -287,8 +119,8 @@ func (c *UploadCommand) Execute(args []string) error { return err } - if err, ok := resp.Error().(*UploadError); ok { - return err + if resp.IsError() { + return resp.Error().(*ResponseError) } fmt.Printf("Response: %s\n", resp.Body())