增加jcsctl程序

This commit is contained in:
Sydonian 2025-06-09 17:13:00 +08:00
parent 15a9bb1642
commit f882b611f4
20 changed files with 356 additions and 196 deletions

View File

@ -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)

View File

@ -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
}

View File

@ -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{})
}

View File

@ -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{})
}
*/

View File

@ -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) {
}

View File

@ -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{})
}

View File

@ -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{})
}

View File

@ -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{})
}
*/

View File

@ -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
}

View File

@ -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() {

View File

@ -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)

View File

@ -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{})
}

View File

@ -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 {

5
jcsctl/cmd/all/all.go Normal file
View File

@ -0,0 +1,5 @@
package all
import (
_ "gitlink.org.cn/cloudream/jcs-pub/jcsctl/cmd/bucket"
)

View File

@ -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)
}

49
jcsctl/cmd/bucket/ls.go Normal file
View File

@ -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)
}
}
}

118
jcsctl/cmd/cmd.go Normal file
View File

@ -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() {
}

16
jcsctl/cmd/utils.go Normal file
View File

@ -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)
}

11
jcsctl/main.go Normal file
View File

@ -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()
}

View File

@ -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",
})
}