534 lines
16 KiB
Go
534 lines
16 KiB
Go
package api
|
||
|
||
import (
|
||
"fmt"
|
||
"io"
|
||
"mime"
|
||
"net/http"
|
||
"net/url"
|
||
"strings"
|
||
"time"
|
||
|
||
"gitlink.org.cn/cloudream/common/consts/errorcode"
|
||
"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/serder"
|
||
jcstypes "gitlink.org.cn/cloudream/jcs-pub/common/types"
|
||
)
|
||
|
||
type ObjectService struct {
|
||
*Client
|
||
}
|
||
|
||
func (c *Client) Object() *ObjectService {
|
||
return &ObjectService{
|
||
Client: c,
|
||
}
|
||
}
|
||
|
||
const ObjectListPathByPath = "/object/listByPath"
|
||
|
||
type ObjectListByPath struct {
|
||
PackageID jcstypes.PackageID `form:"packageID" binding:"required" url:"packageID" json:"packageID"`
|
||
Path string `form:"path" url:"path" json:"path"` // 允许为空字符串
|
||
IsPrefix bool `form:"isPrefix" url:"isPrefix" json:"isPrefix"`
|
||
NoRecursive bool `form:"noRecursive" url:"noRecursive" json:"noRecursive"` // 仅当isPrefix为true时有效,表示仅查询直接属于Prefix下的对象,对于更深的对象,返回它们的公共前缀
|
||
MaxKeys int `form:"maxKeys" url:"maxKeys" json:"maxKeys"`
|
||
ContinuationToken string `form:"continuationToken" url:"continuationToken" json:"continuationToken"` // 用于分页,如果为空字符串,表示从头开始
|
||
}
|
||
|
||
func (r *ObjectListByPath) MakeParam() *sdks.RequestParam {
|
||
return sdks.MakeQueryParam(http.MethodGet, ObjectListPathByPath, r)
|
||
}
|
||
|
||
type ObjectListByPathResp struct {
|
||
CommonPrefixes []string `json:"commonPrefixes"` // 仅在IsPrefix为true且NoRecursive为true时有效,包含更深层对象的shared prefix
|
||
Objects []jcstypes.Object `json:"objects"` // 如果IsPrefix为true且NoRecursive为false,则返回所有匹配的对象,否则只返回直接属于Prefix下的对象
|
||
IsTruncated bool `json:"isTruncated"` // 是否还有更多对象
|
||
NextContinuationToken string `json:"nextContinuationToken"` // 用于分页,如果IsTruncated为true,则下次请求的ContinuationToken为该值
|
||
}
|
||
|
||
func (r *ObjectListByPathResp) ParseResponse(resp *http.Response) error {
|
||
return sdks.ParseCodeDataJSONResponse(resp, r)
|
||
}
|
||
|
||
func (c *ObjectService) ListByPath(req ObjectListByPath) (*ObjectListByPathResp, error) {
|
||
return JSONAPI(&c.cfg, c.httpCli, &req, &ObjectListByPathResp{})
|
||
}
|
||
|
||
const ObjectListByIDsPath = "/object/listByIDs"
|
||
|
||
type ObjectListByIDs struct {
|
||
ObjectIDs []jcstypes.ObjectID `form:"objectIDs" binding:"required" url:"objectIDs"`
|
||
}
|
||
|
||
func (r *ObjectListByIDs) MakeParam() *sdks.RequestParam {
|
||
return sdks.MakeQueryParam(http.MethodGet, ObjectListByIDsPath, r)
|
||
}
|
||
|
||
type ObjectListByIDsResp struct {
|
||
Objects []*jcstypes.Object `json:"object"` // 与ObjectIDs一一对应,如果某个ID不存在,则对应位置为nil
|
||
}
|
||
|
||
func (r *ObjectListByIDsResp) ParseResponse(resp *http.Response) error {
|
||
return sdks.ParseCodeDataJSONResponse(resp, r)
|
||
}
|
||
|
||
func (c *ObjectService) ListByIDs(req ObjectListByIDs) (*ObjectListByIDsResp, error) {
|
||
return JSONAPI(&c.cfg, c.httpCli, &req, &ObjectListByIDsResp{})
|
||
}
|
||
|
||
const ObjectUploadPath = "/object/upload"
|
||
|
||
type ObjectUpload struct {
|
||
Info ObjectUploadInfo
|
||
Files UploadObjectIterator `json:"-"`
|
||
}
|
||
|
||
type ObjectUploadInfo struct {
|
||
PackageID jcstypes.PackageID `json:"packageID" binding:"required"`
|
||
Affinity jcstypes.UserSpaceID `json:"affinity"`
|
||
CopyTo []jcstypes.UserSpaceID `json:"copyTo"`
|
||
CopyToPath []string `json:"copyToPath"`
|
||
}
|
||
|
||
type UploadingObject struct {
|
||
Path string
|
||
File io.ReadCloser
|
||
}
|
||
|
||
type UploadObjectIterator = iterator.Iterator[*UploadingObject]
|
||
|
||
type ObjectUploadResp struct {
|
||
Uploadeds []jcstypes.Object `json:"uploadeds"`
|
||
}
|
||
|
||
func (c *ObjectService) Upload(req ObjectUpload) (*ObjectUploadResp, error) {
|
||
type uploadInfo struct {
|
||
Info string `url:"info"`
|
||
}
|
||
|
||
url, err := url.JoinPath(c.cfg.EndPoint, "v1", ObjectUploadPath)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
infoJSON, err := serder.ObjectToJSON(req.Info)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("upload info to json: %w", err)
|
||
}
|
||
|
||
resp, err := PostMultiPart(&c.cfg, c.httpCli, url,
|
||
uploadInfo{Info: string(infoJSON)},
|
||
iterator.Map(req.Files, func(src *UploadingObject) (*http2.IterMultiPartFile, error) {
|
||
return &http2.IterMultiPartFile{
|
||
FieldName: "files",
|
||
FileName: src.Path,
|
||
File: src.File,
|
||
}, nil
|
||
}))
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
contType := resp.Header.Get("Content-Type")
|
||
if strings.Contains(contType, http2.ContentTypeJSON) {
|
||
var err error
|
||
var codeResp response[ObjectUploadResp]
|
||
if codeResp, err = serder.JSONToObjectStreamEx[response[ObjectUploadResp]](resp.Body); err != nil {
|
||
return nil, fmt.Errorf("parsing response: %w", err)
|
||
}
|
||
|
||
if codeResp.Code == errorcode.OK {
|
||
return &codeResp.Data, nil
|
||
}
|
||
|
||
return nil, codeResp.ToError()
|
||
}
|
||
|
||
return nil, fmt.Errorf("unknow response content type: %s", contType)
|
||
|
||
}
|
||
|
||
const ObjectDownloadPath = "/object/download"
|
||
|
||
type ObjectDownload struct {
|
||
ObjectID jcstypes.ObjectID `form:"objectID" url:"objectID" binding:"required"`
|
||
Offset int64 `form:"offset" url:"offset,omitempty"`
|
||
Length *int64 `form:"length" url:"length,omitempty"`
|
||
}
|
||
|
||
func (r *ObjectDownload) MakeParam() *sdks.RequestParam {
|
||
return sdks.MakeQueryParam(http.MethodGet, ObjectDownloadPath, r)
|
||
}
|
||
|
||
type DownloadingObject struct {
|
||
Path string
|
||
File io.ReadCloser
|
||
}
|
||
|
||
func (c *ObjectService) Download(req ObjectDownload) (*DownloadingObject, error) {
|
||
u, err := url.JoinPath(c.cfg.EndPoint, "v1")
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
httpReq, err := req.MakeParam().MakeRequest(u)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
resp, err := c.httpCli.Do(httpReq)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
contType := resp.Header.Get("Content-Type")
|
||
|
||
if strings.Contains(contType, http2.ContentTypeJSON) {
|
||
var codeResp response[any]
|
||
if err := serder.JSONToObjectStream(resp.Body, &codeResp); err != nil {
|
||
return nil, fmt.Errorf("parsing response: %w", err)
|
||
}
|
||
|
||
return nil, codeResp.ToError()
|
||
}
|
||
|
||
_, params, err := mime.ParseMediaType(resp.Header.Get("Content-Disposition"))
|
||
if err != nil {
|
||
return nil, fmt.Errorf("parsing content disposition: %w", err)
|
||
}
|
||
|
||
return &DownloadingObject{
|
||
Path: params["filename"],
|
||
File: resp.Body,
|
||
}, nil
|
||
}
|
||
|
||
const ObjectDownloadByPathPath = "/object/downloadByPath"
|
||
|
||
type ObjectDownloadByPath struct {
|
||
PackageID jcstypes.PackageID `form:"packageID" url:"packageID" binding:"required"`
|
||
Path string `form:"path" url:"path" binding:"required"`
|
||
Offset int64 `form:"offset" url:"offset,omitempty"`
|
||
Length *int64 `form:"length" url:"length,omitempty"`
|
||
}
|
||
|
||
func (r *ObjectDownloadByPath) MakeParam() *sdks.RequestParam {
|
||
return sdks.MakeQueryParam(http.MethodGet, ObjectDownloadByPathPath, r)
|
||
}
|
||
|
||
func (c *ObjectService) DownloadByPath(req ObjectDownloadByPath) (*DownloadingObject, error) {
|
||
u, err := url.JoinPath(c.cfg.EndPoint, "v1")
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
httpReq, err := req.MakeParam().MakeRequest(u)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
resp, err := c.httpCli.Do(httpReq)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
contType := resp.Header.Get("Content-Type")
|
||
|
||
if strings.Contains(contType, http2.ContentTypeJSON) {
|
||
var codeResp response[any]
|
||
if err := serder.JSONToObjectStream(resp.Body, &codeResp); err != nil {
|
||
return nil, fmt.Errorf("parsing response: %w", err)
|
||
}
|
||
|
||
return nil, codeResp.ToError()
|
||
}
|
||
|
||
_, params, err := mime.ParseMediaType(resp.Header.Get("Content-Disposition"))
|
||
if err != nil {
|
||
return nil, fmt.Errorf("parsing content disposition: %w", err)
|
||
}
|
||
|
||
return &DownloadingObject{
|
||
Path: params["filename"],
|
||
File: resp.Body,
|
||
}, nil
|
||
}
|
||
|
||
const ObjectUpdateInfoPath = "/object/updateInfo"
|
||
|
||
type UpdatingObject struct {
|
||
ObjectID jcstypes.ObjectID `json:"objectID" binding:"required"`
|
||
UpdateTime time.Time `json:"updateTime" binding:"required"`
|
||
}
|
||
|
||
func (u *UpdatingObject) ApplyTo(obj *jcstypes.Object) {
|
||
obj.UpdateTime = u.UpdateTime
|
||
}
|
||
|
||
type ObjectUpdateInfo struct {
|
||
Updatings []UpdatingObject `json:"updatings" binding:"required"`
|
||
}
|
||
|
||
func (r *ObjectUpdateInfo) MakeParam() *sdks.RequestParam {
|
||
return sdks.MakeJSONParam(http.MethodPost, ObjectUpdateInfoPath, r)
|
||
}
|
||
|
||
type ObjectUpdateInfoResp struct {
|
||
Successes []jcstypes.ObjectID `json:"successes"`
|
||
}
|
||
|
||
func (r *ObjectUpdateInfoResp) ParseResponse(resp *http.Response) error {
|
||
return sdks.ParseCodeDataJSONResponse(resp, r)
|
||
}
|
||
|
||
func (c *ObjectService) UpdateInfo(req ObjectUpdateInfo) (*ObjectUpdateInfoResp, error) {
|
||
return JSONAPI(&c.cfg, c.httpCli, &req, &ObjectUpdateInfoResp{})
|
||
}
|
||
|
||
const ObjectUpdateInfoByPathPath = "/object/updateInfoByPath"
|
||
|
||
type ObjectUpdateInfoByPath struct {
|
||
PackageID jcstypes.PackageID `json:"packageID" binding:"required"`
|
||
Path string `json:"path" binding:"required"`
|
||
UpdateTime time.Time `json:"updateTime" binding:"required"`
|
||
}
|
||
|
||
func (r *ObjectUpdateInfoByPath) MakeParam() *sdks.RequestParam {
|
||
return sdks.MakeJSONParam(http.MethodPost, ObjectUpdateInfoByPathPath, r)
|
||
}
|
||
|
||
type ObjectUpdateInfoByPathResp struct{}
|
||
|
||
func (r *ObjectUpdateInfoByPathResp) ParseResponse(resp *http.Response) error {
|
||
return sdks.ParseCodeDataJSONResponse(resp, r)
|
||
}
|
||
|
||
func (c *ObjectService) UpdateInfoByPath(req ObjectUpdateInfoByPath) (*ObjectUpdateInfoByPathResp, error) {
|
||
return JSONAPI(&c.cfg, c.httpCli, &req, &ObjectUpdateInfoByPathResp{})
|
||
}
|
||
|
||
const ObjectMovePath = "/object/move"
|
||
|
||
type MovingObject struct {
|
||
ObjectID jcstypes.ObjectID `json:"objectID" binding:"required"`
|
||
PackageID jcstypes.PackageID `json:"packageID" binding:"required"`
|
||
Path string `json:"path" binding:"required"`
|
||
}
|
||
|
||
func (m *MovingObject) ApplyTo(obj *jcstypes.Object) {
|
||
obj.PackageID = m.PackageID
|
||
obj.Path = m.Path
|
||
}
|
||
|
||
type ObjectMove struct {
|
||
Movings []MovingObject `json:"movings" binding:"required"`
|
||
}
|
||
|
||
func (r *ObjectMove) MakeParam() *sdks.RequestParam {
|
||
return sdks.MakeJSONParam(http.MethodPost, ObjectMovePath, r)
|
||
}
|
||
|
||
type ObjectMoveResp struct {
|
||
Successes []jcstypes.ObjectID `json:"successes"`
|
||
}
|
||
|
||
func (r *ObjectMoveResp) ParseResponse(resp *http.Response) error {
|
||
return sdks.ParseCodeDataJSONResponse(resp, r)
|
||
}
|
||
|
||
func (c *ObjectService) Move(req ObjectMove) (*ObjectMoveResp, error) {
|
||
return JSONAPI(&c.cfg, c.httpCli, &req, &ObjectMoveResp{})
|
||
}
|
||
|
||
const ObjectDeletePath = "/object/delete"
|
||
|
||
type ObjectDelete struct {
|
||
ObjectIDs []jcstypes.ObjectID `json:"objectIDs" binding:"required"`
|
||
}
|
||
|
||
func (r *ObjectDelete) MakeParam() *sdks.RequestParam {
|
||
return sdks.MakeJSONParam(http.MethodPost, ObjectDeletePath, r)
|
||
}
|
||
|
||
type ObjectDeleteResp struct{}
|
||
|
||
func (r *ObjectDeleteResp) ParseResponse(resp *http.Response) error {
|
||
return sdks.ParseCodeDataJSONResponse(resp, r)
|
||
}
|
||
|
||
func (c *ObjectService) Delete(req ObjectDelete) error {
|
||
return JSONAPINoData(&c.cfg, c.httpCli, &req)
|
||
}
|
||
|
||
const ObjectDeleteByPathPath = "/object/deleteByPath"
|
||
|
||
type ObjectDeleteByPath struct {
|
||
PackageID jcstypes.PackageID `json:"packageID" binding:"required"`
|
||
Path string `json:"path" binding:"required"`
|
||
}
|
||
|
||
func (r *ObjectDeleteByPath) MakeParam() *sdks.RequestParam {
|
||
return sdks.MakeJSONParam(http.MethodPost, ObjectDeleteByPathPath, r)
|
||
}
|
||
|
||
type ObjectDeleteByPathResp struct{}
|
||
|
||
func (r *ObjectDeleteByPathResp) ParseResponse(resp *http.Response) error {
|
||
return sdks.ParseCodeDataJSONResponse(resp, r)
|
||
}
|
||
|
||
func (c *ObjectService) DeleteByPath(req ObjectDeleteByPath) error {
|
||
return JSONAPINoData(&c.cfg, c.httpCli, &req)
|
||
}
|
||
|
||
const ObjectClonePath = "/object/clone"
|
||
|
||
type ObjectClone struct {
|
||
Clonings []CloningObject `json:"clonings" binding:"required"`
|
||
}
|
||
|
||
func (r *ObjectClone) MakeParam() *sdks.RequestParam {
|
||
return sdks.MakeJSONParam(http.MethodPost, ObjectClonePath, r)
|
||
}
|
||
|
||
type CloningObject struct {
|
||
ObjectID jcstypes.ObjectID `json:"objectID" binding:"required"`
|
||
NewPath string `json:"newPath" binding:"required"`
|
||
NewPackageID jcstypes.PackageID `json:"newPackageID" binding:"required"`
|
||
}
|
||
|
||
type ObjectCloneResp struct {
|
||
Objects []*jcstypes.Object `json:"objects"`
|
||
}
|
||
|
||
func (r *ObjectCloneResp) ParseResponse(resp *http.Response) error {
|
||
return sdks.ParseCodeDataJSONResponse(resp, r)
|
||
}
|
||
|
||
func (c *ObjectService) Clone(req ObjectClone) (*ObjectCloneResp, error) {
|
||
return JSONAPI(&c.cfg, c.httpCli, &req, &ObjectCloneResp{})
|
||
}
|
||
|
||
const ObjectGetPackageObjectsPath = "/object/getPackageObjects"
|
||
|
||
type ObjectGetPackageObjects struct {
|
||
PackageID jcstypes.PackageID `form:"packageID" url:"packageID" binding:"required"`
|
||
}
|
||
|
||
func (r *ObjectGetPackageObjects) MakeParam() *sdks.RequestParam {
|
||
return sdks.MakeQueryParam(http.MethodGet, ObjectGetPackageObjectsPath, r)
|
||
}
|
||
|
||
type ObjectGetPackageObjectsResp struct {
|
||
Objects []jcstypes.Object `json:"objects"`
|
||
}
|
||
|
||
func (r *ObjectGetPackageObjectsResp) ParseResponse(resp *http.Response) error {
|
||
return sdks.ParseCodeDataJSONResponse(resp, r)
|
||
}
|
||
|
||
func (c *ObjectService) GetPackageObjects(req ObjectGetPackageObjects) (*ObjectGetPackageObjectsResp, error) {
|
||
return JSONAPI(&c.cfg, c.httpCli, &req, &ObjectGetPackageObjectsResp{})
|
||
}
|
||
|
||
const ObjectNewMultipartUploadPath = "/object/newMultipartUpload"
|
||
|
||
type ObjectNewMultipartUpload struct {
|
||
PackageID jcstypes.PackageID `json:"packageID" binding:"required"`
|
||
Path string `json:"path" binding:"required"`
|
||
}
|
||
|
||
func (r *ObjectNewMultipartUpload) MakeParam() *sdks.RequestParam {
|
||
return sdks.MakeJSONParam(http.MethodPost, ObjectNewMultipartUploadPath, r)
|
||
}
|
||
|
||
type ObjectNewMultipartUploadResp struct {
|
||
Object jcstypes.Object `json:"object"`
|
||
}
|
||
|
||
func (r *ObjectNewMultipartUploadResp) ParseResponse(resp *http.Response) error {
|
||
return sdks.ParseCodeDataJSONResponse(resp, r)
|
||
}
|
||
|
||
func (c *ObjectService) NewMultipartUpload(req ObjectNewMultipartUpload) (*ObjectNewMultipartUploadResp, error) {
|
||
return JSONAPI(&c.cfg, c.httpCli, &req, &ObjectNewMultipartUploadResp{})
|
||
}
|
||
|
||
const ObjectUploadPartPath = "/object/uploadPart"
|
||
|
||
type ObjectUploadPart struct {
|
||
ObjectUploadPartInfo
|
||
File io.ReadCloser `json:"-"`
|
||
}
|
||
|
||
type ObjectUploadPartInfo struct {
|
||
ObjectID jcstypes.ObjectID `json:"objectID" binding:"required"`
|
||
Index int `json:"index"`
|
||
}
|
||
|
||
type ObjectUploadPartResp struct{}
|
||
|
||
func (c *ObjectService) UploadPart(req ObjectUploadPart) (*ObjectUploadPartResp, error) {
|
||
url, err := url.JoinPath(c.cfg.EndPoint, ObjectUploadPartPath)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
infoJSON, err := serder.ObjectToJSON(req)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("upload info to json: %w", err)
|
||
}
|
||
|
||
resp, err := http2.PostMultiPart(url, http2.MultiPartRequestParam{
|
||
Form: map[string]string{"info": string(infoJSON)},
|
||
Files: iterator.Array(&http2.IterMultiPartFile{
|
||
FieldName: "file",
|
||
File: req.File,
|
||
}),
|
||
})
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
contType := resp.Header.Get("Content-Type")
|
||
if strings.Contains(contType, http2.ContentTypeJSON) {
|
||
var err error
|
||
var codeResp response[ObjectUploadPartResp]
|
||
if codeResp, err = serder.JSONToObjectStreamEx[response[ObjectUploadPartResp]](resp.Body); err != nil {
|
||
return nil, fmt.Errorf("parsing response: %w", err)
|
||
}
|
||
|
||
if codeResp.Code == errorcode.OK {
|
||
return &codeResp.Data, nil
|
||
}
|
||
|
||
return nil, codeResp.ToError()
|
||
}
|
||
|
||
return nil, fmt.Errorf("unknow response content type: %s", contType)
|
||
}
|
||
|
||
const ObjectCompleteMultipartUploadPath = "/object/completeMultipartUpload"
|
||
|
||
type ObjectCompleteMultipartUpload struct {
|
||
ObjectID jcstypes.ObjectID `json:"objectID" binding:"required"`
|
||
Indexes []int `json:"indexes" binding:"required"`
|
||
}
|
||
|
||
func (r *ObjectCompleteMultipartUpload) MakeParam() *sdks.RequestParam {
|
||
return sdks.MakeJSONParam(http.MethodPost, ObjectCompleteMultipartUploadPath, r)
|
||
}
|
||
|
||
type ObjectCompleteMultipartUploadResp struct {
|
||
Object jcstypes.Object `json:"object"`
|
||
}
|
||
|
||
func (r *ObjectCompleteMultipartUploadResp) ParseResponse(resp *http.Response) error {
|
||
return sdks.ParseCodeDataJSONResponse(resp, r)
|
||
}
|
||
|
||
func (c *ObjectService) CompleteMultipartUpload(req ObjectCompleteMultipartUpload) (*ObjectCompleteMultipartUploadResp, error) {
|
||
return JSONAPI(&c.cfg, c.httpCli, &req, &ObjectCompleteMultipartUploadResp{})
|
||
}
|