diff --git a/client/internal/http/server.go b/client/internal/http/server.go index 8c11c6a..ef8d3c1 100644 --- a/client/internal/http/server.go +++ b/client/internal/http/server.go @@ -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) diff --git a/client/sdk/api/config.go b/client/sdk/api/config.go index dde6e18..389b2fc 100644 --- a/client/sdk/api/config.go +++ b/client/sdk/api/config.go @@ -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 } diff --git a/client/sdk/api/v1/bucket.go b/client/sdk/api/v1/bucket.go index e4d8151..f7d4c10 100644 --- a/client/sdk/api/v1/bucket.go +++ b/client/sdk/api/v1/bucket.go @@ -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{}) } diff --git a/client/sdk/api/v1/cache.go b/client/sdk/api/v1/cache.go index 7d25ee8..8efb2f5 100644 --- a/client/sdk/api/v1/cache.go +++ b/client/sdk/api/v1/cache.go @@ -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{}) } */ diff --git a/client/sdk/api/v1/client.go b/client/sdk/api/v1/client.go index ca7c5a3..6b4d24a 100644 --- a/client/sdk/api/v1/client.go +++ b/client/sdk/api/v1/client.go @@ -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 + cfg api.Config + httpCli *http.Client } -func NewClient(cfg *api.Config) *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"}, + }, + }, + } return &Client{ - cfg: cfg, + cfg: cfg, + httpCli: httpCli, } } - -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, - } -} -func (p *pool) Acquire() (*Client, error) { - cli := NewClient(p.cfg) - return cli, nil -} - -func (p *pool) Release(cli *Client) { - -} diff --git a/client/sdk/api/v1/mount.go b/client/sdk/api/v1/mount.go index b76cf7b..11e8ba3 100644 --- a/client/sdk/api/v1/mount.go +++ b/client/sdk/api/v1/mount.go @@ -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{}) } diff --git a/client/sdk/api/v1/object.go b/client/sdk/api/v1/object.go index 9fd8c43..f10653b 100644 --- a/client/sdk/api/v1/object.go +++ b/client/sdk/api/v1/object.go @@ -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{}) } diff --git a/client/sdk/api/v1/package.go b/client/sdk/api/v1/package.go index 7f793d7..d4f5436 100644 --- a/client/sdk/api/v1/package.go +++ b/client/sdk/api/v1/package.go @@ -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{}) } */ diff --git a/client/sdk/api/v1/presigned.go b/client/sdk/api/v1/presigned.go index 0bda0bd..e98deb6 100644 --- a/client/sdk/api/v1/presigned.go +++ b/client/sdk/api/v1/presigned.go @@ -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) + // 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)) + // us, err := query.Values(req) + // if err != nil { + // return "", err + // } + // us.Add("X-Expires", fmt.Sprintf("%v", expireIn)) - u.RawQuery = us.Encode() + // u.RawQuery = us.Encode() - prod := credentials.NewStaticCredentialsProvider(c.cfg.AccessKey, c.cfg.SecretKey, "") - cred, err := prod.Retrieve(context.TODO()) - if err != nil { - return "", err - } + // 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 - } + // 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 + // signer := v4.NewSigner() + // signedURL, _, err := signer.PresignHTTP(context.Background(), cred, r, "", AuthService, AuthRegion, time.Now()) + // return signedURL, err + return "", nil } diff --git a/client/sdk/api/v1/presigned_test.go b/client/sdk/api/v1/presigned_test.go index 0847d70..cd5ea13 100644 --- a/client/sdk/api/v1/presigned_test.go +++ b/client/sdk/api/v1/presigned_test.go @@ -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() { diff --git a/client/sdk/api/v1/storage_test.go b/client/sdk/api/v1/storage_test.go index 6284e2c..0b3296f 100644 --- a/client/sdk/api/v1/storage_test.go +++ b/client/sdk/api/v1/storage_test.go @@ -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) diff --git a/client/sdk/api/v1/user_space.go b/client/sdk/api/v1/user_space.go index 87511f1..001b618 100644 --- a/client/sdk/api/v1/user_space.go +++ b/client/sdk/api/v1/user_space.go @@ -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{}) } diff --git a/client/sdk/api/v1/utils.go b/client/sdk/api/v1/utils.go index eedae78..8e6ec11 100644 --- a/client/sdk/api/v1/utils.go +++ b/client/sdk/api/v1/utils.go @@ -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 { diff --git a/jcsctl/cmd/all/all.go b/jcsctl/cmd/all/all.go new file mode 100644 index 0000000..9db340d --- /dev/null +++ b/jcsctl/cmd/all/all.go @@ -0,0 +1,5 @@ +package all + +import ( + _ "gitlink.org.cn/cloudream/jcs-pub/jcsctl/cmd/bucket" +) diff --git a/jcsctl/cmd/bucket/bucket.go b/jcsctl/cmd/bucket/bucket.go new file mode 100644 index 0000000..4a6b795 --- /dev/null +++ b/jcsctl/cmd/bucket/bucket.go @@ -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) +} diff --git a/jcsctl/cmd/bucket/ls.go b/jcsctl/cmd/bucket/ls.go new file mode 100644 index 0000000..342155a --- /dev/null +++ b/jcsctl/cmd/bucket/ls.go @@ -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) + } + } +} diff --git a/jcsctl/cmd/cmd.go b/jcsctl/cmd/cmd.go new file mode 100644 index 0000000..ef93a3d --- /dev/null +++ b/jcsctl/cmd/cmd.go @@ -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() { + +} diff --git a/jcsctl/cmd/utils.go b/jcsctl/cmd/utils.go new file mode 100644 index 0000000..b776ac9 --- /dev/null +++ b/jcsctl/cmd/utils.go @@ -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) +} diff --git a/jcsctl/main.go b/jcsctl/main.go new file mode 100644 index 0000000..72e27cd --- /dev/null +++ b/jcsctl/main.go @@ -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() +} diff --git a/magefiles/main.go b/magefiles/main.go index fb34405..0c00179 100644 --- a/magefiles/main.go +++ b/magefiles/main.go @@ -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", + }) +}