| @@ -12,6 +12,7 @@ import ( | |||
| "gitlink.org.cn/cloudream/jcs-pub/client/internal/http/types" | |||
| v1 "gitlink.org.cn/cloudream/jcs-pub/client/internal/http/v1" | |||
| "gitlink.org.cn/cloudream/jcs-pub/client/internal/services" | |||
| "golang.org/x/net/http2" | |||
| ) | |||
| type ServerEventChan = async.UnboundChannel[ServerEvent] | |||
| @@ -58,6 +59,7 @@ func (s *Server) Start() *ServerEventChan { | |||
| s.httpSrv.TLSConfig = &tls.Config{ | |||
| GetConfigForClient: s.auth.TLSConfigSelector, | |||
| } | |||
| http2.ConfigureServer(s.httpSrv, &http2.Server{}) | |||
| s.v1Svr.InitRouters(engine.Group("/v1"), s.auth) | |||
| @@ -1,7 +1,12 @@ | |||
| package api | |||
| import ( | |||
| "crypto/tls" | |||
| "crypto/x509" | |||
| ) | |||
| type Config struct { | |||
| URL string `json:"url"` | |||
| AccessKey string `json:"accessKey"` | |||
| SecretKey string `json:"secretKey"` | |||
| EndPoint string | |||
| RootCA *x509.CertPool | |||
| Cert tls.Certificate | |||
| } | |||
| @@ -34,7 +34,7 @@ func (r *BucketGetByNameResp) ParseResponse(resp *http.Response) error { | |||
| } | |||
| func (c *BucketService) GetByName(req BucketGetByName) (*BucketGetByNameResp, error) { | |||
| return JSONAPI(c.cfg, http.DefaultClient, &req, &BucketGetByNameResp{}) | |||
| return JSONAPI(&c.cfg, c.httpCli, &req, &BucketGetByNameResp{}) | |||
| } | |||
| const BucketCreatePath = "/bucket/create" | |||
| @@ -56,7 +56,7 @@ func (r *BucketCreateResp) ParseResponse(resp *http.Response) error { | |||
| } | |||
| func (c *BucketService) Create(req BucketCreate) (*BucketCreateResp, error) { | |||
| return JSONAPI(c.cfg, http.DefaultClient, &req, &BucketCreateResp{}) | |||
| return JSONAPI(&c.cfg, c.httpCli, &req, &BucketCreateResp{}) | |||
| } | |||
| const BucketDeletePath = "/bucket/delete" | |||
| @@ -76,7 +76,7 @@ func (r *BucketDeleteResp) ParseResponse(resp *http.Response) error { | |||
| } | |||
| func (c *BucketService) Delete(req BucketDelete) error { | |||
| return JSONAPINoData(c.cfg, http.DefaultClient, &req) | |||
| return JSONAPINoData(&c.cfg, c.httpCli, &req) | |||
| } | |||
| const BucketListAllPath = "/bucket/listAll" | |||
| @@ -97,5 +97,5 @@ func (r *BucketListAllResp) ParseResponse(resp *http.Response) error { | |||
| } | |||
| func (c *BucketService) ListAll(req BucketListAll) (*BucketListAllResp, error) { | |||
| return JSONAPI(c.cfg, http.DefaultClient, &req, &BucketListAllResp{}) | |||
| return JSONAPI(&c.cfg, c.httpCli, &req, &BucketListAllResp{}) | |||
| } | |||
| @@ -26,6 +26,6 @@ func (r *CacheMovePackageResp) ParseResponse(resp *http.Response) error { | |||
| } | |||
| func (c *Client) CacheMovePackage(req CacheMovePackageReq) (*CacheMovePackageResp, error) { | |||
| return JSONAPI(c.cfg, http.DefaultClient, &req, &CacheMovePackageResp{}) | |||
| return JSONAPI(&c.cfg, c.httpCli, &req, &CacheMovePackageResp{}) | |||
| } | |||
| */ | |||
| @@ -1,8 +1,13 @@ | |||
| package api | |||
| import ( | |||
| "crypto/tls" | |||
| "net/http" | |||
| "gitlink.org.cn/cloudream/common/sdks" | |||
| "gitlink.org.cn/cloudream/jcs-pub/client/internal/http/auth" | |||
| "gitlink.org.cn/cloudream/jcs-pub/client/sdk/api" | |||
| "golang.org/x/net/http2" | |||
| ) | |||
| type response[T any] struct { | |||
| @@ -19,34 +24,23 @@ func (r *response[T]) ToError() *sdks.CodeMessageError { | |||
| } | |||
| type Client struct { | |||
| cfg *api.Config | |||
| } | |||
| func NewClient(cfg *api.Config) *Client { | |||
| return &Client{ | |||
| cfg: cfg, | |||
| cfg api.Config | |||
| httpCli *http.Client | |||
| } | |||
| func NewClient(cfg api.Config) *Client { | |||
| httpCli := &http.Client{ | |||
| Transport: &http2.Transport{ | |||
| TLSClientConfig: &tls.Config{ | |||
| RootCAs: cfg.RootCA, | |||
| Certificates: []tls.Certificate{cfg.Cert}, | |||
| ServerName: auth.ClientInternalSNI, | |||
| NextProtos: []string{"h2"}, | |||
| }, | |||
| }, | |||
| } | |||
| } | |||
| type Pool interface { | |||
| Acquire() (*Client, error) | |||
| Release(cli *Client) | |||
| } | |||
| type pool struct { | |||
| cfg *api.Config | |||
| } | |||
| func NewPool(cfg *api.Config) Pool { | |||
| return &pool{ | |||
| cfg: cfg, | |||
| return &Client{ | |||
| cfg: cfg, | |||
| httpCli: httpCli, | |||
| } | |||
| } | |||
| func (p *pool) Acquire() (*Client, error) { | |||
| cli := NewClient(p.cfg) | |||
| return cli, nil | |||
| } | |||
| func (p *pool) Release(cli *Client) { | |||
| } | |||
| @@ -34,7 +34,7 @@ func (r *MountDumpStatusResp) ParseResponse(resp *http.Response) error { | |||
| } | |||
| func (c *MountService) DumpStatus(req MountDumpStatus) (*MountDumpStatusResp, error) { | |||
| return JSONAPI(c.cfg, http.DefaultClient, &req, &MountDumpStatusResp{}) | |||
| return JSONAPI(&c.cfg, c.httpCli, &req, &MountDumpStatusResp{}) | |||
| } | |||
| const MountStartReclaimSpacePath = "/mount/startReclaimSpace" | |||
| @@ -52,5 +52,5 @@ func (r *StartMountReclaimSpaceResp) ParseResponse(resp *http.Response) error { | |||
| } | |||
| func (c *MountService) StartReclaimSpace(req StartMountReclaimSpace) (*StartMountReclaimSpaceResp, error) { | |||
| return JSONAPI(c.cfg, http.DefaultClient, &req, &StartMountReclaimSpaceResp{}) | |||
| return JSONAPI(&c.cfg, c.httpCli, &req, &StartMountReclaimSpaceResp{}) | |||
| } | |||
| @@ -1,7 +1,6 @@ | |||
| package api | |||
| import ( | |||
| "context" | |||
| "fmt" | |||
| "io" | |||
| "mime" | |||
| @@ -10,8 +9,6 @@ import ( | |||
| "strings" | |||
| "time" | |||
| v4 "github.com/aws/aws-sdk-go-v2/aws/signer/v4" | |||
| "github.com/aws/aws-sdk-go-v2/credentials" | |||
| "gitlink.org.cn/cloudream/common/consts/errorcode" | |||
| "gitlink.org.cn/cloudream/common/pkgs/iterator" | |||
| "gitlink.org.cn/cloudream/common/sdks" | |||
| @@ -57,7 +54,7 @@ func (r *ObjectListByPathResp) ParseResponse(resp *http.Response) error { | |||
| } | |||
| func (c *ObjectService) ListByPath(req ObjectListByPath) (*ObjectListByPathResp, error) { | |||
| return JSONAPI(c.cfg, http.DefaultClient, &req, &ObjectListByPathResp{}) | |||
| return JSONAPI(&c.cfg, c.httpCli, &req, &ObjectListByPathResp{}) | |||
| } | |||
| const ObjectListByIDsPath = "/object/listByIDs" | |||
| @@ -79,7 +76,7 @@ func (r *ObjectListByIDsResp) ParseResponse(resp *http.Response) error { | |||
| } | |||
| func (c *ObjectService) ListByIDs(req ObjectListByIDs) (*ObjectListByIDsResp, error) { | |||
| return JSONAPI(c.cfg, http.DefaultClient, &req, &ObjectListByIDsResp{}) | |||
| return JSONAPI(&c.cfg, c.httpCli, &req, &ObjectListByIDsResp{}) | |||
| } | |||
| const ObjectUploadPath = "/object/upload" | |||
| @@ -112,7 +109,7 @@ func (c *ObjectService) Upload(req ObjectUpload) (*ObjectUploadResp, error) { | |||
| Info string `url:"info"` | |||
| } | |||
| url, err := url.JoinPath(c.cfg.URL, ObjectUploadPath) | |||
| url, err := url.JoinPath(c.cfg.EndPoint, "v1", ObjectUploadPath) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| @@ -122,7 +119,7 @@ func (c *ObjectService) Upload(req ObjectUpload) (*ObjectUploadResp, error) { | |||
| return nil, fmt.Errorf("upload info to json: %w", err) | |||
| } | |||
| resp, err := PostMultiPart(c.cfg, url, | |||
| resp, err := PostMultiPart(&c.cfg, url, | |||
| uploadInfo{Info: string(infoJSON)}, | |||
| iterator.Map(req.Files, func(src *UploadingObject) (*http2.IterMultiPartFile, error) { | |||
| return &http2.IterMultiPartFile{ | |||
| @@ -172,26 +169,12 @@ type DownloadingObject struct { | |||
| } | |||
| func (c *ObjectService) Download(req ObjectDownload) (*DownloadingObject, error) { | |||
| httpReq, err := req.MakeParam().MakeRequest(c.cfg.URL) | |||
| httpReq, err := req.MakeParam().MakeRequest(c.cfg.EndPoint) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| if c.cfg.AccessKey != "" && c.cfg.SecretKey != "" { | |||
| prod := credentials.NewStaticCredentialsProvider(c.cfg.AccessKey, c.cfg.SecretKey, "") | |||
| cred, err := prod.Retrieve(context.TODO()) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| signer := v4.NewSigner() | |||
| err = signer.SignHTTP(context.Background(), cred, httpReq, "", AuthService, AuthRegion, time.Now()) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| } | |||
| resp, err := http.DefaultClient.Do(httpReq) | |||
| resp, err := c.httpCli.Do(httpReq) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| @@ -232,26 +215,12 @@ func (r *ObjectDownloadByPath) MakeParam() *sdks.RequestParam { | |||
| } | |||
| func (c *ObjectService) DownloadByPath(req ObjectDownloadByPath) (*DownloadingObject, error) { | |||
| httpReq, err := req.MakeParam().MakeRequest(c.cfg.URL) | |||
| httpReq, err := req.MakeParam().MakeRequest(c.cfg.EndPoint) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| if c.cfg.AccessKey != "" && c.cfg.SecretKey != "" { | |||
| prod := credentials.NewStaticCredentialsProvider(c.cfg.AccessKey, c.cfg.SecretKey, "") | |||
| cred, err := prod.Retrieve(context.TODO()) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| signer := v4.NewSigner() | |||
| err = signer.SignHTTP(context.Background(), cred, httpReq, "", AuthService, AuthRegion, time.Now()) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| } | |||
| resp, err := http.DefaultClient.Do(httpReq) | |||
| resp, err := c.httpCli.Do(httpReq) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| @@ -306,7 +275,7 @@ func (r *ObjectUpdateInfoResp) ParseResponse(resp *http.Response) error { | |||
| } | |||
| func (c *ObjectService) UpdateInfo(req ObjectUpdateInfo) (*ObjectUpdateInfoResp, error) { | |||
| return JSONAPI(c.cfg, http.DefaultClient, &req, &ObjectUpdateInfoResp{}) | |||
| return JSONAPI(&c.cfg, c.httpCli, &req, &ObjectUpdateInfoResp{}) | |||
| } | |||
| const ObjectUpdateInfoByPathPath = "/object/updateInfoByPath" | |||
| @@ -328,7 +297,7 @@ func (r *ObjectUpdateInfoByPathResp) ParseResponse(resp *http.Response) error { | |||
| } | |||
| func (c *ObjectService) UpdateInfoByPath(req ObjectUpdateInfoByPath) (*ObjectUpdateInfoByPathResp, error) { | |||
| return JSONAPI(c.cfg, http.DefaultClient, &req, &ObjectUpdateInfoByPathResp{}) | |||
| return JSONAPI(&c.cfg, c.httpCli, &req, &ObjectUpdateInfoByPathResp{}) | |||
| } | |||
| const ObjectMovePath = "/object/move" | |||
| @@ -361,7 +330,7 @@ func (r *ObjectMoveResp) ParseResponse(resp *http.Response) error { | |||
| } | |||
| func (c *ObjectService) Move(req ObjectMove) (*ObjectMoveResp, error) { | |||
| return JSONAPI(c.cfg, http.DefaultClient, &req, &ObjectMoveResp{}) | |||
| return JSONAPI(&c.cfg, c.httpCli, &req, &ObjectMoveResp{}) | |||
| } | |||
| const ObjectDeletePath = "/object/delete" | |||
| @@ -381,7 +350,7 @@ func (r *ObjectDeleteResp) ParseResponse(resp *http.Response) error { | |||
| } | |||
| func (c *ObjectService) Delete(req ObjectDelete) error { | |||
| return JSONAPINoData(c.cfg, http.DefaultClient, &req) | |||
| return JSONAPINoData(&c.cfg, c.httpCli, &req) | |||
| } | |||
| const ObjectDeleteByPathPath = "/object/deleteByPath" | |||
| @@ -402,7 +371,7 @@ func (r *ObjectDeleteByPathResp) ParseResponse(resp *http.Response) error { | |||
| } | |||
| func (c *ObjectService) DeleteByPath(req ObjectDeleteByPath) error { | |||
| return JSONAPINoData(c.cfg, http.DefaultClient, &req) | |||
| return JSONAPINoData(&c.cfg, c.httpCli, &req) | |||
| } | |||
| const ObjectClonePath = "/object/clone" | |||
| @@ -430,7 +399,7 @@ func (r *ObjectCloneResp) ParseResponse(resp *http.Response) error { | |||
| } | |||
| func (c *ObjectService) Clone(req ObjectClone) (*ObjectCloneResp, error) { | |||
| return JSONAPI(c.cfg, http.DefaultClient, &req, &ObjectCloneResp{}) | |||
| return JSONAPI(&c.cfg, c.httpCli, &req, &ObjectCloneResp{}) | |||
| } | |||
| const ObjectGetPackageObjectsPath = "/object/getPackageObjects" | |||
| @@ -452,7 +421,7 @@ func (r *ObjectGetPackageObjectsResp) ParseResponse(resp *http.Response) error { | |||
| } | |||
| func (c *ObjectService) GetPackageObjects(req ObjectGetPackageObjects) (*ObjectGetPackageObjectsResp, error) { | |||
| return JSONAPI(c.cfg, http.DefaultClient, &req, &ObjectGetPackageObjectsResp{}) | |||
| return JSONAPI(&c.cfg, c.httpCli, &req, &ObjectGetPackageObjectsResp{}) | |||
| } | |||
| const ObjectNewMultipartUploadPath = "/object/newMultipartUpload" | |||
| @@ -475,7 +444,7 @@ func (r *ObjectNewMultipartUploadResp) ParseResponse(resp *http.Response) error | |||
| } | |||
| func (c *ObjectService) NewMultipartUpload(req ObjectNewMultipartUpload) (*ObjectNewMultipartUploadResp, error) { | |||
| return JSONAPI(c.cfg, http.DefaultClient, &req, &ObjectNewMultipartUploadResp{}) | |||
| return JSONAPI(&c.cfg, c.httpCli, &req, &ObjectNewMultipartUploadResp{}) | |||
| } | |||
| const ObjectUploadPartPath = "/object/uploadPart" | |||
| @@ -493,7 +462,7 @@ type ObjectUploadPartInfo struct { | |||
| type ObjectUploadPartResp struct{} | |||
| func (c *ObjectService) UploadPart(req ObjectUploadPart) (*ObjectUploadPartResp, error) { | |||
| url, err := url.JoinPath(c.cfg.URL, ObjectUploadPartPath) | |||
| url, err := url.JoinPath(c.cfg.EndPoint, ObjectUploadPartPath) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| @@ -552,5 +521,5 @@ func (r *ObjectCompleteMultipartUploadResp) ParseResponse(resp *http.Response) e | |||
| } | |||
| func (c *ObjectService) CompleteMultipartUpload(req ObjectCompleteMultipartUpload) (*ObjectCompleteMultipartUploadResp, error) { | |||
| return JSONAPI(c.cfg, http.DefaultClient, &req, &ObjectCompleteMultipartUploadResp{}) | |||
| return JSONAPI(&c.cfg, c.httpCli, &req, &ObjectCompleteMultipartUploadResp{}) | |||
| } | |||
| @@ -40,7 +40,7 @@ func (r *PackageGetResp) ParseResponse(resp *http.Response) error { | |||
| } | |||
| func (c *PackageService) Get(req PackageGetReq) (*PackageGetResp, error) { | |||
| return JSONAPI(c.cfg, http.DefaultClient, &req, &PackageGetResp{}) | |||
| return JSONAPI(&c.cfg, c.httpCli, &req, &PackageGetResp{}) | |||
| } | |||
| const PackageGetByFullNamePath = "/package/getByFullName" | |||
| @@ -63,7 +63,7 @@ func (r *PackageGetByFullNameResp) ParseResponse(resp *http.Response) error { | |||
| } | |||
| func (c *PackageService) GetByName(req PackageGetByFullName) (*PackageGetByFullNameResp, error) { | |||
| return JSONAPI(c.cfg, http.DefaultClient, &req, &PackageGetByFullNameResp{}) | |||
| return JSONAPI(&c.cfg, c.httpCli, &req, &PackageGetByFullNameResp{}) | |||
| } | |||
| const PackageCreatePath = "/package/create" | |||
| @@ -86,7 +86,7 @@ func (r *PackageCreateResp) ParseResponse(resp *http.Response) error { | |||
| } | |||
| func (s *PackageService) Create(req PackageCreate) (*PackageCreateResp, error) { | |||
| return JSONAPI(s.cfg, http.DefaultClient, &req, &PackageCreateResp{}) | |||
| return JSONAPI(&s.cfg, s.httpCli, &req, &PackageCreateResp{}) | |||
| } | |||
| const PackageCreateUploadPath = "/package/createUpload" | |||
| @@ -107,7 +107,7 @@ type PackageCreateUploadResp struct { | |||
| } | |||
| func (c *PackageService) CreateUpload(req PackageCreateUpload) (*PackageCreateUploadResp, error) { | |||
| url, err := url.JoinPath(c.cfg.URL, PackageCreateUploadPath) | |||
| url, err := url.JoinPath(c.cfg.EndPoint, "v1", PackageCreateUploadPath) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| @@ -117,7 +117,7 @@ func (c *PackageService) CreateUpload(req PackageCreateUpload) (*PackageCreateUp | |||
| return nil, fmt.Errorf("upload info to json: %w", err) | |||
| } | |||
| resp, err := PostMultiPart(c.cfg, url, | |||
| resp, err := PostMultiPart(&c.cfg, url, | |||
| map[string]string{"info": string(infoJSON)}, | |||
| iterator.Map(req.Files, func(src *UploadingObject) (*http2.IterMultiPartFile, error) { | |||
| return &http2.IterMultiPartFile{ | |||
| @@ -159,7 +159,7 @@ func (r *PackageDeleteResp) ParseResponse(resp *http.Response) error { | |||
| } | |||
| func (c *PackageService) Delete(req PackageDelete) error { | |||
| return JSONAPINoData(c.cfg, http.DefaultClient, &req) | |||
| return JSONAPINoData(&c.cfg, c.httpCli, &req) | |||
| } | |||
| const PackageClonePath = "/package/clone" | |||
| @@ -183,7 +183,7 @@ func (r *PackageCloneResp) ParseResponse(resp *http.Response) error { | |||
| } | |||
| func (c *PackageService) Clone(req PackageClone) (*PackageCloneResp, error) { | |||
| return JSONAPI(c.cfg, http.DefaultClient, &req, &PackageCloneResp{}) | |||
| return JSONAPI(&c.cfg, c.httpCli, &req, &PackageCloneResp{}) | |||
| } | |||
| const PackageListBucketPackagesPath = "/package/listBucketPackages" | |||
| @@ -205,7 +205,7 @@ func (r *PackageListBucketPackagesResp) ParseResponse(resp *http.Response) error | |||
| } | |||
| func (c *PackageService) ListBucketPackages(req PackageListBucketPackages) (*PackageListBucketPackagesResp, error) { | |||
| return JSONAPI(c.cfg, http.DefaultClient, &req, &PackageListBucketPackagesResp{}) | |||
| return JSONAPI(&c.cfg, c.httpCli, &req, &PackageListBucketPackagesResp{}) | |||
| } | |||
| const PackageGetCachedStoragesPath = "/package/getCachedStorages" | |||
| @@ -228,6 +228,6 @@ func (r *PackageGetCachedStoragesResp) ParseResponse(resp *http.Response) error | |||
| } | |||
| func (c *PackageService) GetCachedStorages(req PackageGetCachedStoragesReq) (*PackageGetCachedStoragesResp, error) { | |||
| return JSONAPI(c.cfg, http.DefaultClient, &req, &PackageGetCachedStoragesResp{}) | |||
| return JSONAPI(&c.cfg, c.httpCli, &req, &PackageGetCachedStoragesResp{}) | |||
| } | |||
| */ | |||
| @@ -1,15 +1,8 @@ | |||
| package api | |||
| import ( | |||
| "context" | |||
| "fmt" | |||
| "net/http" | |||
| "net/url" | |||
| "time" | |||
| v4 "github.com/aws/aws-sdk-go-v2/aws/signer/v4" | |||
| "github.com/aws/aws-sdk-go-v2/credentials" | |||
| "github.com/google/go-querystring/query" | |||
| clitypes "gitlink.org.cn/cloudream/jcs-pub/client/types" | |||
| ) | |||
| @@ -120,32 +113,33 @@ func (c *PresignedService) ObjectCompleteMultipartUpload(req PresignedObjectComp | |||
| } | |||
| func (c *PresignedService) presign(req any, path string, method string, expireIn int) (string, error) { | |||
| u, err := url.Parse(c.cfg.URL) | |||
| if err != nil { | |||
| return "", err | |||
| } | |||
| u = u.JoinPath(path) | |||
| us, err := query.Values(req) | |||
| if err != nil { | |||
| return "", err | |||
| } | |||
| us.Add("X-Expires", fmt.Sprintf("%v", expireIn)) | |||
| u.RawQuery = us.Encode() | |||
| prod := credentials.NewStaticCredentialsProvider(c.cfg.AccessKey, c.cfg.SecretKey, "") | |||
| cred, err := prod.Retrieve(context.TODO()) | |||
| if err != nil { | |||
| return "", err | |||
| } | |||
| r, err := http.NewRequest(method, u.String(), nil) | |||
| if err != nil { | |||
| return "", err | |||
| } | |||
| signer := v4.NewSigner() | |||
| signedURL, _, err := signer.PresignHTTP(context.Background(), cred, r, "", AuthService, AuthRegion, time.Now()) | |||
| return signedURL, err | |||
| // u, err := url.Parse(c.cfg.EndPoint) | |||
| // if err != nil { | |||
| // return "", err | |||
| // } | |||
| // u = u.JoinPath(path) | |||
| // us, err := query.Values(req) | |||
| // if err != nil { | |||
| // return "", err | |||
| // } | |||
| // us.Add("X-Expires", fmt.Sprintf("%v", expireIn)) | |||
| // u.RawQuery = us.Encode() | |||
| // prod := credentials.NewStaticCredentialsProvider(c.cfg.AccessKey, c.cfg.SecretKey, "") | |||
| // cred, err := prod.Retrieve(context.TODO()) | |||
| // if err != nil { | |||
| // return "", err | |||
| // } | |||
| // r, err := http.NewRequest(method, u.String(), nil) | |||
| // if err != nil { | |||
| // return "", err | |||
| // } | |||
| // signer := v4.NewSigner() | |||
| // signedURL, _, err := signer.PresignHTTP(context.Background(), cred, r, "", AuthService, AuthRegion, time.Now()) | |||
| // return signedURL, err | |||
| return "", nil | |||
| } | |||
| @@ -9,10 +9,8 @@ import ( | |||
| ) | |||
| func Test_Presigned(t *testing.T) { | |||
| cli := NewClient(&api.Config{ | |||
| URL: "http://localhost:7890", | |||
| AccessKey: "123456", | |||
| SecretKey: "123456", | |||
| cli := NewClient(api.Config{ | |||
| EndPoint: "http://localhost:7890", | |||
| }) | |||
| Convey("下载文件", t, func() { | |||
| @@ -39,10 +37,8 @@ func Test_Presigned(t *testing.T) { | |||
| } | |||
| func Test_PresignedObjectListByPath(t *testing.T) { | |||
| cli := NewClient(&api.Config{ | |||
| URL: "http://localhost:7890", | |||
| AccessKey: "123456", | |||
| SecretKey: "123456", | |||
| cli := NewClient(api.Config{ | |||
| EndPoint: "http://localhost:7890", | |||
| }) | |||
| Convey("下载文件", t, func() { | |||
| @@ -63,10 +59,8 @@ func Test_PresignedObjectListByPath(t *testing.T) { | |||
| } | |||
| func Test_PresignedObjectDownloadByPath(t *testing.T) { | |||
| cli := NewClient(&api.Config{ | |||
| URL: "http://localhost:7890", | |||
| AccessKey: "123456", | |||
| SecretKey: "123456", | |||
| cli := NewClient(api.Config{ | |||
| EndPoint: "http://localhost:7890", | |||
| }) | |||
| Convey("下载文件", t, func() { | |||
| @@ -83,10 +77,8 @@ func Test_PresignedObjectDownloadByPath(t *testing.T) { | |||
| } | |||
| func Test_PresignedObjectDownload(t *testing.T) { | |||
| cli := NewClient(&api.Config{ | |||
| URL: "http://localhost:7890", | |||
| AccessKey: "123456", | |||
| SecretKey: "123456", | |||
| cli := NewClient(api.Config{ | |||
| EndPoint: "http://localhost:7890", | |||
| }) | |||
| Convey("下载文件", t, func() { | |||
| @@ -102,8 +94,8 @@ func Test_PresignedObjectDownload(t *testing.T) { | |||
| } | |||
| func Test_PresignedObjectUpload(t *testing.T) { | |||
| cli := NewClient(&api.Config{ | |||
| URL: "http://localhost:7890", | |||
| cli := NewClient(api.Config{ | |||
| EndPoint: "http://localhost:7890", | |||
| }) | |||
| Convey("上传文件", t, func() { | |||
| @@ -118,8 +110,8 @@ func Test_PresignedObjectUpload(t *testing.T) { | |||
| } | |||
| func Test_PresignedNewMultipartUpload(t *testing.T) { | |||
| cli := NewClient(&api.Config{ | |||
| URL: "http://localhost:7890", | |||
| cli := NewClient(api.Config{ | |||
| EndPoint: "http://localhost:7890", | |||
| }) | |||
| Convey("启动分片上传", t, func() { | |||
| @@ -134,10 +126,8 @@ func Test_PresignedNewMultipartUpload(t *testing.T) { | |||
| } | |||
| func Test_PresignedObjectUploadPart(t *testing.T) { | |||
| cli := NewClient(&api.Config{ | |||
| URL: "http://localhost:7890", | |||
| AccessKey: "123456", | |||
| SecretKey: "123456", | |||
| cli := NewClient(api.Config{ | |||
| EndPoint: "http://localhost:7890", | |||
| }) | |||
| Convey("上传分片", t, func() { | |||
| @@ -152,10 +142,8 @@ func Test_PresignedObjectUploadPart(t *testing.T) { | |||
| } | |||
| func Test_PresignedCompleteMultipartUpload(t *testing.T) { | |||
| cli := NewClient(&api.Config{ | |||
| URL: "http://localhost:7890", | |||
| AccessKey: "123456", | |||
| SecretKey: "123456", | |||
| cli := NewClient(api.Config{ | |||
| EndPoint: "http://localhost:7890", | |||
| }) | |||
| Convey("合并分片", t, func() { | |||
| @@ -15,8 +15,8 @@ import ( | |||
| func Test_PackageGet(t *testing.T) { | |||
| Convey("上传后获取Package信息", t, func() { | |||
| cli := NewClient(&api.Config{ | |||
| URL: "http://localhost:7890", | |||
| cli := NewClient(api.Config{ | |||
| EndPoint: "http://localhost:7890", | |||
| }) | |||
| fileData := make([]byte, 4096) | |||
| @@ -65,8 +65,8 @@ func Test_PackageGet(t *testing.T) { | |||
| func Test_Object(t *testing.T) { | |||
| Convey("上传,下载,删除", t, func() { | |||
| cli := NewClient(&api.Config{ | |||
| URL: "http://localhost:7890", | |||
| cli := NewClient(api.Config{ | |||
| EndPoint: "http://localhost:7890", | |||
| }) | |||
| fileData := make([]byte, 4096) | |||
| @@ -120,8 +120,8 @@ func Test_Object(t *testing.T) { | |||
| func Test_ObjectList(t *testing.T) { | |||
| Convey("路径查询", t, func() { | |||
| cli := NewClient(&api.Config{ | |||
| URL: "http://localhost:7890", | |||
| cli := NewClient(api.Config{ | |||
| EndPoint: "http://localhost:7890", | |||
| }) | |||
| resp, err := cli.Object().ListByPath(ObjectListByPath{ | |||
| @@ -136,8 +136,8 @@ func Test_ObjectList(t *testing.T) { | |||
| func Test_Storage(t *testing.T) { | |||
| Convey("上传后调度文件", t, func() { | |||
| cli := NewClient(&api.Config{ | |||
| URL: "http://localhost:7890", | |||
| cli := NewClient(api.Config{ | |||
| EndPoint: "http://localhost:7890", | |||
| }) | |||
| fileData := make([]byte, 4096) | |||
| @@ -233,10 +233,8 @@ func Test_Storage(t *testing.T) { | |||
| */ | |||
| func Test_Sign(t *testing.T) { | |||
| Convey("签名接口", t, func() { | |||
| cli := NewClient(&api.Config{ | |||
| URL: "http://localhost:7890/v1", | |||
| AccessKey: "123456", | |||
| SecretKey: "123456", | |||
| cli := NewClient(api.Config{ | |||
| EndPoint: "http://localhost:7890/v1", | |||
| }) | |||
| fileData := make([]byte, 4096) | |||
| @@ -27,7 +27,7 @@ func (r *UserSpaceDownloadPackageResp) ParseResponse(resp *http.Response) error | |||
| } | |||
| func (c *Client) UserSpaceDownloadPackage(req UserSpaceDownloadPackageReq) (*UserSpaceDownloadPackageResp, error) { | |||
| return JSONAPI(c.cfg, http.DefaultClient, &req, &UserSpaceDownloadPackageResp{}) | |||
| return JSONAPI(&c.cfg, c.httpCli, &req, &UserSpaceDownloadPackageResp{}) | |||
| } | |||
| const UserSpaceCreatePackagePath = "/userspace/createPackage" | |||
| @@ -53,7 +53,7 @@ func (r *UserSpaceCreatePackageResp) ParseResponse(resp *http.Response) error { | |||
| } | |||
| func (c *Client) UserSpaceCreatePackage(req UserSpaceCreatePackageReq) (*UserSpaceCreatePackageResp, error) { | |||
| return JSONAPI(c.cfg, http.DefaultClient, &req, &UserSpaceCreatePackageResp{}) | |||
| return JSONAPI(&c.cfg, c.httpCli, &req, &UserSpaceCreatePackageResp{}) | |||
| } | |||
| const UserSpaceGetPath = "/userspace/get" | |||
| @@ -75,7 +75,7 @@ func (r *UserSpaceGetResp) ParseResponse(resp *http.Response) error { | |||
| } | |||
| func (c *Client) UserSpaceGet(req UserSpaceGet) (*UserSpaceGetResp, error) { | |||
| return JSONAPI(c.cfg, http.DefaultClient, &req, &UserSpaceGetResp{}) | |||
| return JSONAPI(&c.cfg, c.httpCli, &req, &UserSpaceGetResp{}) | |||
| } | |||
| // 创建用户空间 | |||
| @@ -103,7 +103,7 @@ func (r *UserSpaceCreateResp) ParseResponse(resp *http.Response) error { | |||
| } | |||
| func (c *Client) UserSpaceCreate(req UserSpaceCreate) (*UserSpaceCreateResp, error) { | |||
| return JSONAPI(c.cfg, http.DefaultClient, &req, &UserSpaceCreateResp{}) | |||
| return JSONAPI(&c.cfg, c.httpCli, &req, &UserSpaceCreateResp{}) | |||
| } | |||
| // 更新用户空间。一些重要的配置不可再二次修改 | |||
| @@ -129,7 +129,7 @@ func (r *UserSpaceUpdateResp) ParseResponse(resp *http.Response) error { | |||
| } | |||
| func (c *Client) UserSpaceUpdate(req UserSpaceUpdate) (*UserSpaceUpdateResp, error) { | |||
| return JSONAPI(c.cfg, http.DefaultClient, &req, &UserSpaceUpdateResp{}) | |||
| return JSONAPI(&c.cfg, c.httpCli, &req, &UserSpaceUpdateResp{}) | |||
| } | |||
| // 删除用户空间 | |||
| @@ -150,7 +150,7 @@ func (r *UserSpaceDeleteResp) ParseResponse(resp *http.Response) error { | |||
| } | |||
| func (c *Client) UserSpaceDelete(req UserSpaceDelete) (*UserSpaceDeleteResp, error) { | |||
| return JSONAPI(c.cfg, http.DefaultClient, &req, &UserSpaceDeleteResp{}) | |||
| return JSONAPI(&c.cfg, c.httpCli, &req, &UserSpaceDeleteResp{}) | |||
| } | |||
| // 测试给定用户空间的配置是否有效 | |||
| @@ -175,7 +175,7 @@ func (r *UserSpaceTestResp) ParseResponse(resp *http.Response) error { | |||
| } | |||
| func (c *Client) UserSpaceTest(req UserSpaceTest) (*UserSpaceTestResp, error) { | |||
| return JSONAPI(c.cfg, http.DefaultClient, &req, &UserSpaceTestResp{}) | |||
| return JSONAPI(&c.cfg, c.httpCli, &req, &UserSpaceTestResp{}) | |||
| } | |||
| // 存储服务间直传 | |||
| @@ -201,5 +201,5 @@ func (r *UserSpaceSpaceToSpaceResp) ParseResponse(resp *http.Response) error { | |||
| } | |||
| func (c *Client) UserSpaceSpaceToSpace(req UserSpaceSpaceToSpace) (*UserSpaceSpaceToSpaceResp, error) { | |||
| return JSONAPI(c.cfg, http.DefaultClient, &req, &UserSpaceSpaceToSpaceResp{}) | |||
| return JSONAPI(&c.cfg, c.httpCli, &req, &UserSpaceSpaceToSpaceResp{}) | |||
| } | |||
| @@ -7,8 +7,8 @@ import ( | |||
| "io" | |||
| "mime/multipart" | |||
| "net/http" | |||
| "net/url" | |||
| ul "net/url" | |||
| "path/filepath" | |||
| "strings" | |||
| "github.com/google/go-querystring/query" | |||
| @@ -20,10 +20,6 @@ import ( | |||
| "gitlink.org.cn/cloudream/jcs-pub/client/sdk/api" | |||
| ) | |||
| 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") | |||
| @@ -49,16 +45,14 @@ func JSONAPI[Resp sdks.APIResponse, Req sdks.APIRequest](cfg *api.Config, cli *h | |||
| param := req.MakeParam() | |||
| httpReq, err := param.MakeRequest(cfg.URL) | |||
| v1EndPoint, err := url.JoinPath(cfg.EndPoint, "v1") | |||
| 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 | |||
| } | |||
| httpReq, err := param.MakeRequest(v1EndPoint) | |||
| if err != nil { | |||
| return resp, err | |||
| } | |||
| httpResp, err := cli.Do(httpReq) | |||
| @@ -73,16 +67,14 @@ func JSONAPI[Resp sdks.APIResponse, Req sdks.APIRequest](cfg *api.Config, cli *h | |||
| func JSONAPINoData[Req sdks.APIRequest](cfg *api.Config, cli *http.Client, req Req) error { | |||
| param := req.MakeParam() | |||
| httpReq, err := param.MakeRequest(cfg.URL) | |||
| v1EndPoint, err := url.JoinPath(cfg.EndPoint, "v1") | |||
| 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 | |||
| } | |||
| httpReq, err := param.MakeRequest(v1EndPoint) | |||
| if err != nil { | |||
| return err | |||
| } | |||
| resp, err := cli.Do(httpReq) | |||
| @@ -166,13 +158,6 @@ func PostMultiPart(cfg *api.Config, url string, info any, files http2.MultiPartF | |||
| 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 { | |||
| @@ -0,0 +1,5 @@ | |||
| package all | |||
| import ( | |||
| _ "gitlink.org.cn/cloudream/jcs-pub/jcsctl/cmd/bucket" | |||
| ) | |||
| @@ -0,0 +1,14 @@ | |||
| package bucket | |||
| import ( | |||
| "github.com/spf13/cobra" | |||
| "gitlink.org.cn/cloudream/jcs-pub/jcsctl/cmd" | |||
| ) | |||
| var BucketCmd = &cobra.Command{ | |||
| Use: "bucket", | |||
| } | |||
| func init() { | |||
| cmd.RootCmd.AddCommand(BucketCmd) | |||
| } | |||
| @@ -0,0 +1,49 @@ | |||
| package bucket | |||
| import ( | |||
| "fmt" | |||
| "github.com/jedib0t/go-pretty/v6/table" | |||
| "github.com/spf13/cobra" | |||
| "gitlink.org.cn/cloudream/jcs-pub/client/sdk/api/v1" | |||
| "gitlink.org.cn/cloudream/jcs-pub/jcsctl/cmd" | |||
| ) | |||
| func init() { | |||
| var opt lsOpt | |||
| cmd := cobra.Command{ | |||
| Use: "ls", | |||
| Run: func(c *cobra.Command, args []string) { | |||
| ctx := cmd.GetCmdCtx(c) | |||
| ls(c, ctx, opt) | |||
| }, | |||
| } | |||
| cmd.Flags().BoolVarP(&opt.Long, "", "l", false, "listing in long format") | |||
| BucketCmd.AddCommand(&cmd) | |||
| } | |||
| type lsOpt struct { | |||
| Long bool | |||
| } | |||
| func ls(c *cobra.Command, ctx *cmd.CommandContext, opt lsOpt) { | |||
| resp, err := ctx.Client.Bucket().ListAll(api.BucketListAll{}) | |||
| if err != nil { | |||
| cmd.ErrorExitln(err.Error()) | |||
| } | |||
| if opt.Long { | |||
| fmt.Printf("total: %d\n", len(resp.Buckets)) | |||
| tb := table.NewWriter() | |||
| tb.AppendHeader(table.Row{"Bucket ID", "Name", "Create Time"}) | |||
| for _, b := range resp.Buckets { | |||
| tb.AppendRow(table.Row{b.BucketID, b.Name, b.CreateTime}) | |||
| } | |||
| fmt.Println(tb.Render()) | |||
| } else { | |||
| for _, b := range resp.Buckets { | |||
| fmt.Println(b.Name) | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,118 @@ | |||
| package cmd | |||
| import ( | |||
| "context" | |||
| "crypto/tls" | |||
| "crypto/x509" | |||
| "fmt" | |||
| "os" | |||
| "path/filepath" | |||
| "github.com/spf13/cobra" | |||
| "gitlink.org.cn/cloudream/jcs-pub/client/sdk/api" | |||
| cliapi "gitlink.org.cn/cloudream/jcs-pub/client/sdk/api/v1" | |||
| ) | |||
| const ( | |||
| defaultCAFileName = "ca_cert.pem" | |||
| defaultCertFileName = "client_cert.pem" | |||
| defaultKeyFileName = "client_key.pem" | |||
| ) | |||
| var RootCmd = cobra.Command{} | |||
| type CommandContext struct { | |||
| Client *cliapi.Client | |||
| RootCA *x509.CertPool | |||
| Cert tls.Certificate | |||
| } | |||
| func GetCmdCtx(cmd *cobra.Command) *CommandContext { | |||
| return cmd.Context().Value("cmdCtx").(*CommandContext) | |||
| } | |||
| func RootExecute() { | |||
| var ca string | |||
| var cert string | |||
| var key string | |||
| var endpoint string | |||
| RootCmd.Flags().StringVar(&ca, "ca", "", "CA certificate file path") | |||
| RootCmd.Flags().StringVar(&cert, "cert", "", "client certificate file path") | |||
| RootCmd.Flags().StringVar(&key, "key", "", "client key file path") | |||
| RootCmd.Flags().StringVarP(&endpoint, "endpoint", "e", "", "API endpoint") | |||
| RootCmd.MarkFlagsRequiredTogether("ca", "cert", "key") | |||
| if ca == "" { | |||
| certDir := searchCertDir() | |||
| if certDir == "" { | |||
| fmt.Printf("cert files not found, please specify --ca, --cert and --key\n") | |||
| os.Exit(1) | |||
| } | |||
| ca = filepath.Join(certDir, defaultCAFileName) | |||
| cert = filepath.Join(certDir, defaultCertFileName) | |||
| key = filepath.Join(certDir, defaultKeyFileName) | |||
| } | |||
| rootCAPool := x509.NewCertPool() | |||
| rootCAPem, err := os.ReadFile(ca) | |||
| if err != nil { | |||
| fmt.Printf("reading CA file: %v\n", err) | |||
| os.Exit(1) | |||
| } | |||
| if !rootCAPool.AppendCertsFromPEM(rootCAPem) { | |||
| fmt.Printf("parsing CA failed") | |||
| os.Exit(1) | |||
| } | |||
| clientCert, err := tls.LoadX509KeyPair(cert, key) | |||
| if err != nil { | |||
| fmt.Printf("loading client cert/key: %v\n", err) | |||
| os.Exit(1) | |||
| } | |||
| if endpoint == "" { | |||
| endpoint = "https://127.0.0.1:7890" | |||
| } | |||
| cli := cliapi.NewClient(api.Config{ | |||
| EndPoint: endpoint, | |||
| RootCA: rootCAPool, | |||
| Cert: clientCert, | |||
| }) | |||
| RootCmd.ExecuteContext(context.WithValue(context.Background(), "cmdCtx", &CommandContext{ | |||
| Client: cli, | |||
| RootCA: rootCAPool, | |||
| Cert: clientCert, | |||
| })) | |||
| } | |||
| func searchCertDir() string { | |||
| execPath, err := os.Executable() | |||
| if err == nil { | |||
| execDir := filepath.Dir(execPath) | |||
| ca, err := os.Stat(filepath.Join(execDir, defaultCAFileName)) | |||
| if err == nil && !ca.IsDir() { | |||
| return execDir | |||
| } | |||
| } | |||
| workDir, err := os.Getwd() | |||
| if err == nil { | |||
| ca, err := os.Stat(filepath.Join(workDir, defaultCAFileName)) | |||
| if err == nil && !ca.IsDir() { | |||
| return workDir | |||
| } | |||
| } | |||
| return "" | |||
| } | |||
| func loadCert() { | |||
| } | |||
| @@ -0,0 +1,16 @@ | |||
| package cmd | |||
| import ( | |||
| "fmt" | |||
| "os" | |||
| ) | |||
| func ErrorExitf(format string, args ...interface{}) { | |||
| fmt.Printf(format, args...) | |||
| os.Exit(1) | |||
| } | |||
| func ErrorExitln(msg string) { | |||
| fmt.Println(msg) | |||
| os.Exit(1) | |||
| } | |||
| @@ -0,0 +1,11 @@ | |||
| package main | |||
| import ( | |||
| "gitlink.org.cn/cloudream/jcs-pub/jcsctl/cmd" | |||
| _ "gitlink.org.cn/cloudream/jcs-pub/jcsctl/cmd/all" | |||
| _ "google.golang.org/grpc/balancer/grpclb" | |||
| ) | |||
| func main() { | |||
| cmd.RootExecute() | |||
| } | |||
| @@ -46,6 +46,9 @@ func Bin() error { | |||
| if err := Coordinator(); err != nil { | |||
| return err | |||
| } | |||
| if err := Jcsctl(); err != nil { | |||
| return err | |||
| } | |||
| return nil | |||
| } | |||
| @@ -122,3 +125,12 @@ func Coordinator() error { | |||
| EntryFile: "coordinator/main.go", | |||
| }) | |||
| } | |||
| func Jcsctl() error { | |||
| return magefiles.Build(magefiles.BuildArgs{ | |||
| OutputName: "jcsctl", | |||
| OutputDir: "jcsctl", | |||
| AssetsDir: "assets", | |||
| EntryFile: "jcsctl/main.go", | |||
| }) | |||
| } | |||