增加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" "gitlink.org.cn/cloudream/jcs-pub/client/internal/http/types"
v1 "gitlink.org.cn/cloudream/jcs-pub/client/internal/http/v1" v1 "gitlink.org.cn/cloudream/jcs-pub/client/internal/http/v1"
"gitlink.org.cn/cloudream/jcs-pub/client/internal/services" "gitlink.org.cn/cloudream/jcs-pub/client/internal/services"
"golang.org/x/net/http2"
) )
type ServerEventChan = async.UnboundChannel[ServerEvent] type ServerEventChan = async.UnboundChannel[ServerEvent]
@ -58,6 +59,7 @@ func (s *Server) Start() *ServerEventChan {
s.httpSrv.TLSConfig = &tls.Config{ s.httpSrv.TLSConfig = &tls.Config{
GetConfigForClient: s.auth.TLSConfigSelector, GetConfigForClient: s.auth.TLSConfigSelector,
} }
http2.ConfigureServer(s.httpSrv, &http2.Server{})
s.v1Svr.InitRouters(engine.Group("/v1"), s.auth) s.v1Svr.InitRouters(engine.Group("/v1"), s.auth)

View File

@ -1,7 +1,12 @@
package api package api
import (
"crypto/tls"
"crypto/x509"
)
type Config struct { type Config struct {
URL string `json:"url"` EndPoint string
AccessKey string `json:"accessKey"` RootCA *x509.CertPool
SecretKey string `json:"secretKey"` 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) { 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" const BucketCreatePath = "/bucket/create"
@ -56,7 +56,7 @@ func (r *BucketCreateResp) ParseResponse(resp *http.Response) error {
} }
func (c *BucketService) Create(req BucketCreate) (*BucketCreateResp, 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" const BucketDeletePath = "/bucket/delete"
@ -76,7 +76,7 @@ func (r *BucketDeleteResp) ParseResponse(resp *http.Response) error {
} }
func (c *BucketService) Delete(req BucketDelete) 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" const BucketListAllPath = "/bucket/listAll"
@ -97,5 +97,5 @@ func (r *BucketListAllResp) ParseResponse(resp *http.Response) error {
} }
func (c *BucketService) ListAll(req BucketListAll) (*BucketListAllResp, 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) { 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 package api
import ( import (
"crypto/tls"
"net/http"
"gitlink.org.cn/cloudream/common/sdks" "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" "gitlink.org.cn/cloudream/jcs-pub/client/sdk/api"
"golang.org/x/net/http2"
) )
type response[T any] struct { type response[T any] struct {
@ -19,34 +24,23 @@ func (r *response[T]) ToError() *sdks.CodeMessageError {
} }
type Client struct { 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{ 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) { 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" const MountStartReclaimSpacePath = "/mount/startReclaimSpace"
@ -52,5 +52,5 @@ func (r *StartMountReclaimSpaceResp) ParseResponse(resp *http.Response) error {
} }
func (c *MountService) StartReclaimSpace(req StartMountReclaimSpace) (*StartMountReclaimSpaceResp, 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 package api
import ( import (
"context"
"fmt" "fmt"
"io" "io"
"mime" "mime"
@ -10,8 +9,6 @@ import (
"strings" "strings"
"time" "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/consts/errorcode"
"gitlink.org.cn/cloudream/common/pkgs/iterator" "gitlink.org.cn/cloudream/common/pkgs/iterator"
"gitlink.org.cn/cloudream/common/sdks" "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) { 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" const ObjectListByIDsPath = "/object/listByIDs"
@ -79,7 +76,7 @@ func (r *ObjectListByIDsResp) ParseResponse(resp *http.Response) error {
} }
func (c *ObjectService) ListByIDs(req ObjectListByIDs) (*ObjectListByIDsResp, 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" const ObjectUploadPath = "/object/upload"
@ -112,7 +109,7 @@ func (c *ObjectService) Upload(req ObjectUpload) (*ObjectUploadResp, error) {
Info string `url:"info"` Info string `url:"info"`
} }
url, err := url.JoinPath(c.cfg.URL, ObjectUploadPath) url, err := url.JoinPath(c.cfg.EndPoint, "v1", ObjectUploadPath)
if err != nil { if err != nil {
return nil, err 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) 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)}, uploadInfo{Info: string(infoJSON)},
iterator.Map(req.Files, func(src *UploadingObject) (*http2.IterMultiPartFile, error) { iterator.Map(req.Files, func(src *UploadingObject) (*http2.IterMultiPartFile, error) {
return &http2.IterMultiPartFile{ return &http2.IterMultiPartFile{
@ -172,26 +169,12 @@ type DownloadingObject struct {
} }
func (c *ObjectService) Download(req ObjectDownload) (*DownloadingObject, error) { 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 { if err != nil {
return nil, err return nil, err
} }
if c.cfg.AccessKey != "" && c.cfg.SecretKey != "" { resp, err := c.httpCli.Do(httpReq)
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)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -232,26 +215,12 @@ func (r *ObjectDownloadByPath) MakeParam() *sdks.RequestParam {
} }
func (c *ObjectService) DownloadByPath(req ObjectDownloadByPath) (*DownloadingObject, error) { 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 { if err != nil {
return nil, err return nil, err
} }
if c.cfg.AccessKey != "" && c.cfg.SecretKey != "" { resp, err := c.httpCli.Do(httpReq)
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)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -306,7 +275,7 @@ func (r *ObjectUpdateInfoResp) ParseResponse(resp *http.Response) error {
} }
func (c *ObjectService) UpdateInfo(req ObjectUpdateInfo) (*ObjectUpdateInfoResp, 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" const ObjectUpdateInfoByPathPath = "/object/updateInfoByPath"
@ -328,7 +297,7 @@ func (r *ObjectUpdateInfoByPathResp) ParseResponse(resp *http.Response) error {
} }
func (c *ObjectService) UpdateInfoByPath(req ObjectUpdateInfoByPath) (*ObjectUpdateInfoByPathResp, 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" const ObjectMovePath = "/object/move"
@ -361,7 +330,7 @@ func (r *ObjectMoveResp) ParseResponse(resp *http.Response) error {
} }
func (c *ObjectService) Move(req ObjectMove) (*ObjectMoveResp, 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" const ObjectDeletePath = "/object/delete"
@ -381,7 +350,7 @@ func (r *ObjectDeleteResp) ParseResponse(resp *http.Response) error {
} }
func (c *ObjectService) Delete(req ObjectDelete) 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" const ObjectDeleteByPathPath = "/object/deleteByPath"
@ -402,7 +371,7 @@ func (r *ObjectDeleteByPathResp) ParseResponse(resp *http.Response) error {
} }
func (c *ObjectService) DeleteByPath(req ObjectDeleteByPath) 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" const ObjectClonePath = "/object/clone"
@ -430,7 +399,7 @@ func (r *ObjectCloneResp) ParseResponse(resp *http.Response) error {
} }
func (c *ObjectService) Clone(req ObjectClone) (*ObjectCloneResp, 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" const ObjectGetPackageObjectsPath = "/object/getPackageObjects"
@ -452,7 +421,7 @@ func (r *ObjectGetPackageObjectsResp) ParseResponse(resp *http.Response) error {
} }
func (c *ObjectService) GetPackageObjects(req ObjectGetPackageObjects) (*ObjectGetPackageObjectsResp, 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" const ObjectNewMultipartUploadPath = "/object/newMultipartUpload"
@ -475,7 +444,7 @@ func (r *ObjectNewMultipartUploadResp) ParseResponse(resp *http.Response) error
} }
func (c *ObjectService) NewMultipartUpload(req ObjectNewMultipartUpload) (*ObjectNewMultipartUploadResp, 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" const ObjectUploadPartPath = "/object/uploadPart"
@ -493,7 +462,7 @@ type ObjectUploadPartInfo struct {
type ObjectUploadPartResp struct{} type ObjectUploadPartResp struct{}
func (c *ObjectService) UploadPart(req ObjectUploadPart) (*ObjectUploadPartResp, error) { 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 { if err != nil {
return nil, err return nil, err
} }
@ -552,5 +521,5 @@ func (r *ObjectCompleteMultipartUploadResp) ParseResponse(resp *http.Response) e
} }
func (c *ObjectService) CompleteMultipartUpload(req ObjectCompleteMultipartUpload) (*ObjectCompleteMultipartUploadResp, error) { 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) { 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" const PackageGetByFullNamePath = "/package/getByFullName"
@ -63,7 +63,7 @@ func (r *PackageGetByFullNameResp) ParseResponse(resp *http.Response) error {
} }
func (c *PackageService) GetByName(req PackageGetByFullName) (*PackageGetByFullNameResp, 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" const PackageCreatePath = "/package/create"
@ -86,7 +86,7 @@ func (r *PackageCreateResp) ParseResponse(resp *http.Response) error {
} }
func (s *PackageService) Create(req PackageCreate) (*PackageCreateResp, 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" const PackageCreateUploadPath = "/package/createUpload"
@ -107,7 +107,7 @@ type PackageCreateUploadResp struct {
} }
func (c *PackageService) CreateUpload(req PackageCreateUpload) (*PackageCreateUploadResp, error) { 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 { if err != nil {
return nil, err return nil, err
} }
@ -117,7 +117,7 @@ func (c *PackageService) CreateUpload(req PackageCreateUpload) (*PackageCreateUp
return nil, fmt.Errorf("upload info to json: %w", err) 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)}, map[string]string{"info": string(infoJSON)},
iterator.Map(req.Files, func(src *UploadingObject) (*http2.IterMultiPartFile, error) { iterator.Map(req.Files, func(src *UploadingObject) (*http2.IterMultiPartFile, error) {
return &http2.IterMultiPartFile{ return &http2.IterMultiPartFile{
@ -159,7 +159,7 @@ func (r *PackageDeleteResp) ParseResponse(resp *http.Response) error {
} }
func (c *PackageService) Delete(req PackageDelete) 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" const PackageClonePath = "/package/clone"
@ -183,7 +183,7 @@ func (r *PackageCloneResp) ParseResponse(resp *http.Response) error {
} }
func (c *PackageService) Clone(req PackageClone) (*PackageCloneResp, 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" const PackageListBucketPackagesPath = "/package/listBucketPackages"
@ -205,7 +205,7 @@ func (r *PackageListBucketPackagesResp) ParseResponse(resp *http.Response) error
} }
func (c *PackageService) ListBucketPackages(req PackageListBucketPackages) (*PackageListBucketPackagesResp, 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" const PackageGetCachedStoragesPath = "/package/getCachedStorages"
@ -228,6 +228,6 @@ func (r *PackageGetCachedStoragesResp) ParseResponse(resp *http.Response) error
} }
func (c *PackageService) GetCachedStorages(req PackageGetCachedStoragesReq) (*PackageGetCachedStoragesResp, 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 package api
import ( import (
"context"
"fmt"
"net/http" "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" 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) { func (c *PresignedService) presign(req any, path string, method string, expireIn int) (string, error) {
u, err := url.Parse(c.cfg.URL) // u, err := url.Parse(c.cfg.EndPoint)
if err != nil { // if err != nil {
return "", err // return "", err
} // }
u = u.JoinPath(path) // u = u.JoinPath(path)
us, err := query.Values(req) // us, err := query.Values(req)
if err != nil { // if err != nil {
return "", err // return "", err
} // }
us.Add("X-Expires", fmt.Sprintf("%v", expireIn)) // 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, "") // prod := credentials.NewStaticCredentialsProvider(c.cfg.AccessKey, c.cfg.SecretKey, "")
cred, err := prod.Retrieve(context.TODO()) // cred, err := prod.Retrieve(context.TODO())
if err != nil { // if err != nil {
return "", err // return "", err
} // }
r, err := http.NewRequest(method, u.String(), nil) // r, err := http.NewRequest(method, u.String(), nil)
if err != nil { // if err != nil {
return "", err // return "", err
} // }
signer := v4.NewSigner() // signer := v4.NewSigner()
signedURL, _, err := signer.PresignHTTP(context.Background(), cred, r, "", AuthService, AuthRegion, time.Now()) // signedURL, _, err := signer.PresignHTTP(context.Background(), cred, r, "", AuthService, AuthRegion, time.Now())
return signedURL, err // return signedURL, err
return "", nil
} }

View File

@ -9,10 +9,8 @@ import (
) )
func Test_Presigned(t *testing.T) { func Test_Presigned(t *testing.T) {
cli := NewClient(&api.Config{ cli := NewClient(api.Config{
URL: "http://localhost:7890", EndPoint: "http://localhost:7890",
AccessKey: "123456",
SecretKey: "123456",
}) })
Convey("下载文件", t, func() { Convey("下载文件", t, func() {
@ -39,10 +37,8 @@ func Test_Presigned(t *testing.T) {
} }
func Test_PresignedObjectListByPath(t *testing.T) { func Test_PresignedObjectListByPath(t *testing.T) {
cli := NewClient(&api.Config{ cli := NewClient(api.Config{
URL: "http://localhost:7890", EndPoint: "http://localhost:7890",
AccessKey: "123456",
SecretKey: "123456",
}) })
Convey("下载文件", t, func() { Convey("下载文件", t, func() {
@ -63,10 +59,8 @@ func Test_PresignedObjectListByPath(t *testing.T) {
} }
func Test_PresignedObjectDownloadByPath(t *testing.T) { func Test_PresignedObjectDownloadByPath(t *testing.T) {
cli := NewClient(&api.Config{ cli := NewClient(api.Config{
URL: "http://localhost:7890", EndPoint: "http://localhost:7890",
AccessKey: "123456",
SecretKey: "123456",
}) })
Convey("下载文件", t, func() { Convey("下载文件", t, func() {
@ -83,10 +77,8 @@ func Test_PresignedObjectDownloadByPath(t *testing.T) {
} }
func Test_PresignedObjectDownload(t *testing.T) { func Test_PresignedObjectDownload(t *testing.T) {
cli := NewClient(&api.Config{ cli := NewClient(api.Config{
URL: "http://localhost:7890", EndPoint: "http://localhost:7890",
AccessKey: "123456",
SecretKey: "123456",
}) })
Convey("下载文件", t, func() { Convey("下载文件", t, func() {
@ -102,8 +94,8 @@ func Test_PresignedObjectDownload(t *testing.T) {
} }
func Test_PresignedObjectUpload(t *testing.T) { func Test_PresignedObjectUpload(t *testing.T) {
cli := NewClient(&api.Config{ cli := NewClient(api.Config{
URL: "http://localhost:7890", EndPoint: "http://localhost:7890",
}) })
Convey("上传文件", t, func() { Convey("上传文件", t, func() {
@ -118,8 +110,8 @@ func Test_PresignedObjectUpload(t *testing.T) {
} }
func Test_PresignedNewMultipartUpload(t *testing.T) { func Test_PresignedNewMultipartUpload(t *testing.T) {
cli := NewClient(&api.Config{ cli := NewClient(api.Config{
URL: "http://localhost:7890", EndPoint: "http://localhost:7890",
}) })
Convey("启动分片上传", t, func() { Convey("启动分片上传", t, func() {
@ -134,10 +126,8 @@ func Test_PresignedNewMultipartUpload(t *testing.T) {
} }
func Test_PresignedObjectUploadPart(t *testing.T) { func Test_PresignedObjectUploadPart(t *testing.T) {
cli := NewClient(&api.Config{ cli := NewClient(api.Config{
URL: "http://localhost:7890", EndPoint: "http://localhost:7890",
AccessKey: "123456",
SecretKey: "123456",
}) })
Convey("上传分片", t, func() { Convey("上传分片", t, func() {
@ -152,10 +142,8 @@ func Test_PresignedObjectUploadPart(t *testing.T) {
} }
func Test_PresignedCompleteMultipartUpload(t *testing.T) { func Test_PresignedCompleteMultipartUpload(t *testing.T) {
cli := NewClient(&api.Config{ cli := NewClient(api.Config{
URL: "http://localhost:7890", EndPoint: "http://localhost:7890",
AccessKey: "123456",
SecretKey: "123456",
}) })
Convey("合并分片", t, func() { Convey("合并分片", t, func() {

View File

@ -15,8 +15,8 @@ import (
func Test_PackageGet(t *testing.T) { func Test_PackageGet(t *testing.T) {
Convey("上传后获取Package信息", t, func() { Convey("上传后获取Package信息", t, func() {
cli := NewClient(&api.Config{ cli := NewClient(api.Config{
URL: "http://localhost:7890", EndPoint: "http://localhost:7890",
}) })
fileData := make([]byte, 4096) fileData := make([]byte, 4096)
@ -65,8 +65,8 @@ func Test_PackageGet(t *testing.T) {
func Test_Object(t *testing.T) { func Test_Object(t *testing.T) {
Convey("上传,下载,删除", t, func() { Convey("上传,下载,删除", t, func() {
cli := NewClient(&api.Config{ cli := NewClient(api.Config{
URL: "http://localhost:7890", EndPoint: "http://localhost:7890",
}) })
fileData := make([]byte, 4096) fileData := make([]byte, 4096)
@ -120,8 +120,8 @@ func Test_Object(t *testing.T) {
func Test_ObjectList(t *testing.T) { func Test_ObjectList(t *testing.T) {
Convey("路径查询", t, func() { Convey("路径查询", t, func() {
cli := NewClient(&api.Config{ cli := NewClient(api.Config{
URL: "http://localhost:7890", EndPoint: "http://localhost:7890",
}) })
resp, err := cli.Object().ListByPath(ObjectListByPath{ resp, err := cli.Object().ListByPath(ObjectListByPath{
@ -136,8 +136,8 @@ func Test_ObjectList(t *testing.T) {
func Test_Storage(t *testing.T) { func Test_Storage(t *testing.T) {
Convey("上传后调度文件", t, func() { Convey("上传后调度文件", t, func() {
cli := NewClient(&api.Config{ cli := NewClient(api.Config{
URL: "http://localhost:7890", EndPoint: "http://localhost:7890",
}) })
fileData := make([]byte, 4096) fileData := make([]byte, 4096)
@ -233,10 +233,8 @@ func Test_Storage(t *testing.T) {
*/ */
func Test_Sign(t *testing.T) { func Test_Sign(t *testing.T) {
Convey("签名接口", t, func() { Convey("签名接口", t, func() {
cli := NewClient(&api.Config{ cli := NewClient(api.Config{
URL: "http://localhost:7890/v1", EndPoint: "http://localhost:7890/v1",
AccessKey: "123456",
SecretKey: "123456",
}) })
fileData := make([]byte, 4096) 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) { 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" const UserSpaceCreatePackagePath = "/userspace/createPackage"
@ -53,7 +53,7 @@ func (r *UserSpaceCreatePackageResp) ParseResponse(resp *http.Response) error {
} }
func (c *Client) UserSpaceCreatePackage(req UserSpaceCreatePackageReq) (*UserSpaceCreatePackageResp, 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" const UserSpaceGetPath = "/userspace/get"
@ -75,7 +75,7 @@ func (r *UserSpaceGetResp) ParseResponse(resp *http.Response) error {
} }
func (c *Client) UserSpaceGet(req UserSpaceGet) (*UserSpaceGetResp, 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) { 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) { 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) { 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) { 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) { 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" "io"
"mime/multipart" "mime/multipart"
"net/http" "net/http"
"net/url"
ul "net/url" ul "net/url"
"path/filepath"
"strings" "strings"
"github.com/google/go-querystring/query" "github.com/google/go-querystring/query"
@ -20,10 +20,6 @@ import (
"gitlink.org.cn/cloudream/jcs-pub/client/sdk/api" "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) { func ParseJSONResponse[TBody any](resp *http.Response) (TBody, error) {
var ret TBody var ret TBody
contType := resp.Header.Get("Content-Type") contType := resp.Header.Get("Content-Type")
@ -49,17 +45,15 @@ func JSONAPI[Resp sdks.APIResponse, Req sdks.APIRequest](cfg *api.Config, cli *h
param := req.MakeParam() param := req.MakeParam()
httpReq, err := param.MakeRequest(cfg.URL) v1EndPoint, err := url.JoinPath(cfg.EndPoint, "v1")
if err != nil { if err != nil {
return resp, err return resp, err
} }
if cfg.AccessKey != "" && cfg.SecretKey != "" { httpReq, err := param.MakeRequest(v1EndPoint)
err = SignWithPayloadHash(httpReq, calcSha256(param.Body), cfg.AccessKey, cfg.SecretKey)
if err != nil { if err != nil {
return resp, err return resp, err
} }
}
httpResp, err := cli.Do(httpReq) httpResp, err := cli.Do(httpReq)
if err != nil { if err != nil {
@ -73,17 +67,15 @@ 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 { func JSONAPINoData[Req sdks.APIRequest](cfg *api.Config, cli *http.Client, req Req) error {
param := req.MakeParam() param := req.MakeParam()
httpReq, err := param.MakeRequest(cfg.URL) v1EndPoint, err := url.JoinPath(cfg.EndPoint, "v1")
if err != nil { if err != nil {
return err return err
} }
if cfg.AccessKey != "" && cfg.SecretKey != "" { httpReq, err := param.MakeRequest(v1EndPoint)
err = SignWithPayloadHash(httpReq, calcSha256(param.Body), cfg.AccessKey, cfg.SecretKey)
if err != nil { if err != nil {
return err return err
} }
}
resp, err := cli.Do(httpReq) resp, err := cli.Do(httpReq)
if err != nil { if err != nil {
@ -166,13 +158,6 @@ func PostMultiPart(cfg *api.Config, url string, info any, files http2.MultiPartF
req.Body = pr req.Body = pr
if cfg.AccessKey != "" && cfg.SecretKey != "" {
err = SignWithoutBody(req, cfg.AccessKey, cfg.SecretKey)
if err != nil {
return nil, err
}
}
cli := http.Client{} cli := http.Client{}
resp, err := cli.Do(req) resp, err := cli.Do(req)
if err != nil { 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 { if err := Coordinator(); err != nil {
return err return err
} }
if err := Jcsctl(); err != nil {
return err
}
return nil return nil
} }
@ -122,3 +125,12 @@ func Coordinator() error {
EntryFile: "coordinator/main.go", EntryFile: "coordinator/main.go",
}) })
} }
func Jcsctl() error {
return magefiles.Build(magefiles.BuildArgs{
OutputName: "jcsctl",
OutputDir: "jcsctl",
AssetsDir: "assets",
EntryFile: "jcsctl/main.go",
})
}