package cdsapi import ( "crypto/sha256" "encoding/hex" "fmt" "io" "mime/multipart" "net/http" ul "net/url" "path/filepath" "strings" "github.com/google/go-querystring/query" "gitlink.org.cn/cloudream/common/pkgs/iterator" "gitlink.org.cn/cloudream/common/sdks" "gitlink.org.cn/cloudream/common/utils/http2" "gitlink.org.cn/cloudream/common/utils/math2" "gitlink.org.cn/cloudream/common/utils/serder" ) func MakeIPFSFilePath(fileHash string) string { return filepath.Join("ipfs", fileHash) } func ParseJSONResponse[TBody any](resp *http.Response) (TBody, error) { var ret TBody contType := resp.Header.Get("Content-Type") if strings.Contains(contType, http2.ContentTypeJSON) { var err error if ret, err = serder.JSONToObjectStreamEx[TBody](resp.Body); err != nil { return ret, fmt.Errorf("parsing response: %w", err) } return ret, nil } cont, err := io.ReadAll(resp.Body) if err != nil { return ret, fmt.Errorf("unknow response content type: %s, status: %d", contType, resp.StatusCode) } strCont := string(cont) return ret, fmt.Errorf("unknow response content type: %s, status: %d, body(prefix): %s", contType, resp.StatusCode, strCont[:math2.Min(len(strCont), 200)]) } func JSONAPI[Resp sdks.APIResponse, Req sdks.APIRequest](cfg *Config, cli *http.Client, req Req, resp Resp) (Resp, error) { param := req.MakeParam() httpReq, err := param.MakeRequest(cfg.URL) if err != nil { return resp, err } if cfg.AccessKey != "" && cfg.SecretKey != "" { err = SignWithPayloadHash(httpReq, calcSha256(param.Body), cfg.AccessKey, cfg.SecretKey) if err != nil { return resp, err } } httpResp, err := cli.Do(httpReq) if err != nil { return resp, err } err = resp.ParseResponse(httpResp) return resp, err } func JSONAPINoData[Req sdks.APIRequest](cfg *Config, cli *http.Client, req Req) error { param := req.MakeParam() httpReq, err := param.MakeRequest(cfg.URL) if err != nil { return err } if cfg.AccessKey != "" && cfg.SecretKey != "" { err = SignWithPayloadHash(httpReq, calcSha256(param.Body), cfg.AccessKey, cfg.SecretKey) if err != nil { return err } } resp, err := cli.Do(httpReq) if err != nil { return err } return sdks.ParseCodeDataJSONResponse(resp, any(nil)) } func calcSha256(body sdks.RequestBody) string { hasher := sha256.New() switch body := body.(type) { case *sdks.StringBody: hasher.Write([]byte(body.Value)) return hex.EncodeToString(hasher.Sum(nil)) case *sdks.BytesBody: hasher.Write(body.Value) return hex.EncodeToString(hasher.Sum(nil)) case *sdks.StreamBody: return "" default: hash := sha256.Sum256([]byte("")) return hex.EncodeToString(hash[:]) } } func PostMultiPart(cfg *Config, url string, info any, files http2.MultiPartFileIterator) (*http.Response, error) { req, err := http.NewRequest(http.MethodPost, url, nil) if err != nil { return nil, err } pr, pw := io.Pipe() muWriter := multipart.NewWriter(pw) req.Header.Set("Content-Type", fmt.Sprintf("%s;boundary=%s", http2.ContentTypeMultiPart, muWriter.Boundary())) writeResult := make(chan error, 1) go func() { writeResult <- func() error { defer pw.Close() defer muWriter.Close() if info != nil { mp, err := query.Values(info) if err != nil { return fmt.Errorf("formValues object to map failed, err: %w", err) } for k, v := range mp { err := muWriter.WriteField(k, v[0]) if err != nil { return fmt.Errorf("write form field failed, err: %w", err) } } } for { file, err := files.MoveNext() if err == iterator.ErrNoMoreItem { break } if err != nil { return fmt.Errorf("opening file: %w", err) } err = sendFileOnePart(muWriter, file.FieldName, file.FileName, file.File) file.File.Close() if err != nil { return err } } return nil }() }() req.Body = pr if cfg.AccessKey != "" && cfg.SecretKey != "" { err = SignWithoutBody(req, cfg.AccessKey, cfg.SecretKey) if err != nil { return nil, err } } cli := http.Client{} resp, err := cli.Do(req) if err != nil { return nil, err } writeErr := <-writeResult if writeErr != nil { return nil, writeErr } return resp, nil } func sendFileOnePart(muWriter *multipart.Writer, fieldName, fileName string, file io.ReadCloser) error { w, err := muWriter.CreateFormFile(fieldName, ul.PathEscape(fileName)) if err != nil { return fmt.Errorf("create form file failed, err: %w", err) } _, err = io.Copy(w, file) return err }