云存储接入客户端交互

This commit is contained in:
songjc 2025-06-23 11:30:34 +08:00
parent f882b611f4
commit d8ce73cd29
14 changed files with 1300 additions and 1 deletions

View File

@ -48,6 +48,8 @@ func (s *Server) InitRouters(rt gin.IRoutes, ah *auth.Auth) {
rt.POST(cliapi.UserSpaceDownloadPackagePath, certAuth, s.UserSpace().DownloadPackage)
rt.POST(cliapi.UserSpaceCreatePackagePath, certAuth, s.UserSpace().CreatePackage)
rt.GET(cliapi.UserSpaceGetPath, certAuth, s.UserSpace().Get)
rt.GET(cliapi.UserSpaceGetByNamePath, certAuth, s.UserSpace().GetByName)
rt.GET(cliapi.UserSpaceGetAllPath, certAuth, s.UserSpace().GetAll)
rt.POST(cliapi.UserSpaceCreatePath, certAuth, s.UserSpace().Create)
rt.POST(cliapi.UserSpaceUpdatePath, certAuth, s.UserSpace().Update)
rt.POST(cliapi.UserSpaceDeletePath, certAuth, s.UserSpace().Delete)

View File

@ -85,6 +85,43 @@ func (s *UserSpaceService) Get(ctx *gin.Context) {
}))
}
func (s *UserSpaceService) GetByName(ctx *gin.Context) {
log := logger.WithField("HTTP", "UserSpace.GetByName")
var req cliapi.UserSpaceGetByName
if err := ctx.ShouldBindQuery(&req); err != nil {
log.Warnf("binding query: %s", err.Error())
ctx.JSON(http.StatusBadRequest, types.Failed(ecode.BadArgument, "missing argument or invalid argument"))
return
}
info, err := s.svc.UserSpaceSvc().GetByName(req.Name)
if err != nil {
log.Warnf("getting info: %s", err.Error())
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, "get userspace info failed"))
return
}
ctx.JSON(http.StatusOK, types.OK(cliapi.UserSpaceGetByNameResp{
UserSpace: info,
}))
}
func (s *UserSpaceService) GetAll(ctx *gin.Context) {
log := logger.WithField("HTTP", "UserSpace.GetAll")
allInfos, err := s.svc.UserSpaceSvc().GetAll()
if err != nil {
log.Warnf("getting info: %s", err.Error())
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, "get userspaces info failed"))
return
}
ctx.JSON(http.StatusOK, types.OK(cliapi.UserSpaceGetAllResp{
UserSpaces: allInfos,
}))
}
func (s *UserSpaceService) Create(ctx *gin.Context) {
log := logger.WithField("HTTP", "UserSpace.Create")

View File

@ -41,6 +41,10 @@ func (svc *UserSpaceService) GetByName(name string) (clitypes.UserSpace, error)
return svc.DB.UserSpace().GetByName(svc.DB.DefCtx(), name)
}
func (svc *UserSpaceService) GetAll() ([]clitypes.UserSpace, error) {
return svc.DB.UserSpace().GetAll(svc.DB.DefCtx())
}
func (svc *UserSpaceService) Create(req cliapi.UserSpaceCreate) (*cliapi.UserSpaceCreateResp, *ecode.CodeError) {
db2 := svc.DB
space, err := db.DoTx01(db2, func(tx db.SQLContext) (clitypes.UserSpace, error) {

View File

@ -78,6 +78,49 @@ func (c *Client) UserSpaceGet(req UserSpaceGet) (*UserSpaceGetResp, error) {
return JSONAPI(&c.cfg, c.httpCli, &req, &UserSpaceGetResp{})
}
const UserSpaceGetByNamePath = "/userspace/getByName"
type UserSpaceGetByName struct {
Name string `form:"name" url:"name" binding:"required"`
}
func (r *UserSpaceGetByName) MakeParam() *sdks.RequestParam {
return sdks.MakeQueryParam(http.MethodGet, UserSpaceGetByNamePath, r)
}
type UserSpaceGetByNameResp struct {
clitypes.UserSpace
}
func (r *UserSpaceGetByNameResp) ParseResponse(resp *http.Response) error {
return sdks.ParseCodeDataJSONResponse(resp, r)
}
func (c *Client) UserSpaceGetByName(req UserSpaceGetByName) (*UserSpaceGetByNameResp, error) {
return JSONAPI(&c.cfg, c.httpCli, &req, &UserSpaceGetByNameResp{})
}
const UserSpaceGetAllPath = "/userspace/getAll"
type UserSpaceGetAll struct{}
func (r *UserSpaceGetAll) MakeParam() *sdks.RequestParam {
return sdks.MakeQueryParam(http.MethodGet, UserSpaceGetAllPath, nil)
}
type UserSpaceGetAllResp struct {
UserSpaces []clitypes.UserSpace `json:"userSpaces"`
}
func (r *UserSpaceGetAllResp) ParseResponse(resp *http.Response) error {
return sdks.ParseCodeDataJSONResponse(resp, r)
}
func (c *Client) UserSpaceGetAll() (*UserSpaceGetAllResp, error) {
req := UserSpaceGetAll{}
return JSONAPI(&c.cfg, c.httpCli, &req, &UserSpaceGetAllResp{})
}
// 创建用户空间
const UserSpaceCreatePath = "/userspace/create"

View File

@ -1,12 +1,16 @@
package types
import (
"fmt"
"gitlink.org.cn/cloudream/common/pkgs/types"
"gitlink.org.cn/cloudream/common/utils/serder"
)
type StorageCredential interface {
// fmt.Stringer
GetStorageCredentialType() string
String(showPassword bool) string
}
var _ = serder.UseTypeUnionInternallyTagged(types.Ref(types.NewTypeUnion[StorageCredential](
@ -26,6 +30,10 @@ type LocalCred struct {
RootDir string `json:"rootDir"`
}
func (c *LocalCred) String(showPassword bool) string {
return fmt.Sprintf("RootDir=%s", c.RootDir)
}
// type MashupCred struct {
// StorageCredential `json:"-"`
// serder.Metadata `union:"Mashup"`
@ -41,6 +49,14 @@ type OSSCred struct {
SK string `json:"secretAccessKey"`
}
func (c *OSSCred) String(showPassword bool) string {
maskedSK := "****"
if showPassword {
maskedSK = c.SK
}
return fmt.Sprintf("AK=%s, SK=%s", c.AK, maskedSK)
}
type OBSCred struct {
StorageCredential `json:"-"`
serder.Metadata `union:"OBS"`
@ -49,6 +65,14 @@ type OBSCred struct {
SK string `json:"secretAccessKey"`
}
func (c *OBSCred) String(showPassword bool) string {
maskedSK := "****"
if showPassword {
maskedSK = c.SK
}
return fmt.Sprintf("AK=%s, SK=%s", c.AK, maskedSK)
}
type COSCred struct {
StorageCredential `json:"-"`
serder.Metadata `union:"COS"`
@ -57,6 +81,14 @@ type COSCred struct {
SK string `json:"secretAccessKey"`
}
func (c *COSCred) String(showPassword bool) string {
maskedSK := "****"
if showPassword {
maskedSK = c.SK
}
return fmt.Sprintf("AK=%s, SK=%s", c.AK, maskedSK)
}
type EFileCred struct {
StorageCredential `json:"-"`
serder.Metadata `union:"EFile"`
@ -69,6 +101,14 @@ type EFileCred struct {
OrgID string `json:"orgID"`
}
func (c *EFileCred) String(showPassword bool) string {
maskedSK := "****"
if showPassword {
maskedSK = c.Password
}
return fmt.Sprintf("TokenURL=%s, APIURL=%s, TokenExpire=%d, User=%s, Password=%s, OrgID=%s", c.TokenURL, c.APIURL, c.TokenExpire, c.User, maskedSK, c.OrgID)
}
// 通用的S3协议的存储服务
type S3Cred struct {
StorageCredential `json:"-"`
@ -77,3 +117,11 @@ type S3Cred struct {
AK string `json:"accessKeyId"`
SK string `json:"secretAccessKey"`
}
func (c *S3Cred) String(showPassword bool) string {
maskedSK := "****"
if showPassword {
maskedSK = c.SK
}
return fmt.Sprintf("AK=%s, SK=%s", c.AK, maskedSK)
}

15
go.mod
View File

@ -49,19 +49,31 @@ require (
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.6 // indirect
github.com/aws/smithy-go v1.22.2 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/bytedance/sonic v1.11.6 // indirect
github.com/bytedance/sonic/loader v0.1.1 // indirect
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
github.com/charmbracelet/lipgloss v1.1.0 // indirect
github.com/charmbracelet/x/ansi v0.8.0 // indirect
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect
github.com/charmbracelet/x/term v0.2.1 // indirect
github.com/cloudwego/base64x v0.1.4 // indirect
github.com/cloudwego/iasm v0.2.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
github.com/gabriel-vasile/mimetype v1.4.6 // indirect
github.com/goccy/go-json v0.10.3 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/jonboulle/clockwork v0.4.0 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-localereader v0.0.1 // indirect
github.com/mattn/go-tty v0.0.3 // indirect
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/muesli/termenv v0.16.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/pkg/term v1.2.0-beta.2 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
@ -69,6 +81,7 @@ require (
github.com/rogpeppe/go-internal v1.12.0 // indirect
github.com/tjfoc/gmsm v1.4.1 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
go.mongodb.org/mongo-driver v1.12.0 // indirect
golang.org/x/arch v0.8.0 // indirect
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa // indirect
@ -81,6 +94,8 @@ require (
require (
github.com/antonfisher/nested-logrus-formatter v1.3.1 // indirect
github.com/c-bata/go-prompt v0.2.6
github.com/charmbracelet/bubbletea v1.3.5
github.com/chzyer/readline v1.5.1
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect

34
go.sum
View File

@ -28,6 +28,8 @@ github.com/aws/aws-sdk-go-v2/service/s3 v1.71.0 h1:nyuzXooUNJexRT0Oy0UQY6AhOzxPx
github.com/aws/aws-sdk-go-v2/service/s3 v1.71.0/go.mod h1:sT/iQz8JK3u/5gZkT+Hmr7GzVZehUMkRZpOaAwYXeGY=
github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ=
github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
@ -35,6 +37,22 @@ github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4
github.com/c-bata/go-prompt v0.2.6 h1:POP+nrHE+DfLYx370bedwNhsqmpCUynWPxuHi0C5vZI=
github.com/c-bata/go-prompt v0.2.6/go.mod h1:/LMAke8wD2FsNu9EXNdHxNLbd9MedkPnCdfpU9wwHfY=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/charmbracelet/bubbletea v1.3.5 h1:JAMNLTbqMOhSwoELIr0qyP4VidFq72/6E9j7HHmRKQc=
github.com/charmbracelet/bubbletea v1.3.5/go.mod h1:TkCnmH+aBd4LrXhXcqrKiYwRs7qyQx5rBgH5fVY3v54=
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs=
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk=
github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
github.com/charmbracelet/x/ansi v0.8.0 h1:9GTq3xq9caJW8ZrBTe0LIe2fvfLR/bYXKTx2llXn7xE=
github.com/charmbracelet/x/ansi v0.8.0/go.mod h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q=
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8=
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI=
github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
@ -50,6 +68,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8Yc
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
github.com/gabriel-vasile/mimetype v1.4.6 h1:3+PzJTKLkvgjeTbts6msPJt4DixhT4YtFNf1gtGe3zc=
github.com/gabriel-vasile/mimetype v1.4.6/go.mod h1:JX1qVKqZd40hUPpAfiNTe0Sne7hdfKSbOqqmkq8GCXc=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
@ -143,6 +163,8 @@ github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348 h1:MtvEpTB6LX3v
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/magefile/mage v1.15.0 h1:BvGheCMAsG3bWUDbZ8AyXXpCNwU9u5CB6sM+HNb9HYg=
github.com/magefile/mage v1.15.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
@ -155,6 +177,8 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
@ -171,6 +195,12 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
@ -225,6 +255,8 @@ github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZ
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/zyedidia/generic v1.2.1 h1:Zv5KS/N2m0XZZiuLS82qheRG4X1o5gsWreGb0hR7XDc=
@ -291,6 +323,8 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

View File

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

View File

@ -74,7 +74,7 @@ func RootExecute() {
}
if endpoint == "" {
endpoint = "https://127.0.0.1:7890"
endpoint = "https://127.0.0.1:8890"
}
cli := cliapi.NewClient(api.Config{

View File

@ -0,0 +1,563 @@
package userspace
import (
"fmt"
"strconv"
"strings"
"github.com/chzyer/readline"
"github.com/spf13/cobra"
"gitlink.org.cn/cloudream/jcs-pub/client/sdk/api/v1"
cortypes "gitlink.org.cn/cloudream/jcs-pub/coordinator/types"
"gitlink.org.cn/cloudream/jcs-pub/jcsctl/cmd"
)
type MyUserSpace struct {
api.UserSpaceCreate
}
func init() {
cmd := cobra.Command{
Use: "create",
Short: "add a new cloud storage",
Run: func(c *cobra.Command, args []string) {
ctx := cmd.GetCmdCtx(c)
create(c, ctx)
},
}
UserSpaceCmd.AddCommand(&cmd)
}
func create(c *cobra.Command, ctx *cmd.CommandContext) {
var userSpace MyUserSpace
rl, err := readline.New("> ")
if err != nil {
fmt.Printf("初始化命令行失败: %v\n", err)
return
}
defer rl.Close()
rl.SetPrompt("\033[36m请输入存储服务名称(Name): \033[0m")
name, err := rl.Readline()
if err != nil {
return
}
userSpace.Name = name
storageType, err := promptSelectStorage()
if err != nil {
return
}
switch storageType {
case "Local":
err = userSpace.collectLocalConfig(rl)
case "OBS":
err = userSpace.collectObsConfig(rl)
case "OSS":
err = userSpace.collectOssConfig(rl)
case "COS":
err = userSpace.collectCosConfig(rl)
case "EFile":
err = userSpace.collectEfileConfig(rl)
case "S3":
err = userSpace.collectS3Config(rl)
}
if err != nil {
return
}
err = userSpace.collectShardStore(rl)
if err != nil {
return
}
err = userSpace.collectWorkingDir(rl)
if err != nil {
return
}
_, err = ctx.Client.UserSpaceCreate(userSpace.UserSpaceCreate)
if err != nil {
fmt.Printf("\033[31m保存配置失败: %v\033[0m", err)
return
}
fmt.Println("\033[32m配置保存成功!\033[0m")
}
func promptSelectStorage() (string, error) {
rl, _ := readline.NewEx(&readline.Config{
Prompt: "\033[36m»\033[0m ",
HistoryFile: "/tmp/storage_history.txt",
InterruptPrompt: "^C",
})
defer rl.Close()
fmt.Println("\033[1;36m请选择存储类型(StorageType):\033[0m")
options := []string{"Local", "OBS", "OSS", "COS", "EFile", "S3"}
for i, option := range options {
fmt.Printf("\033[33m%d. %s\033[0m\n", i+1, option)
}
for {
line, err := rl.Readline()
if err != nil {
return "", err
}
trimmed := strings.TrimSpace(line)
switch trimmed {
case "1":
return "Local", nil
case "2":
return "OBS", nil
case "3":
return "OSS", nil
case "4":
return "COS", nil
case "5":
return "EFile", nil
case "6":
return "S3", nil
default:
fmt.Printf("\033[31m错误: 无效选项 '%s',请输入序号!\033[0m\n", line)
}
}
}
func (userSpace *MyUserSpace) collectLocalConfig(rl *readline.Instance) error {
var err error
rl.SetPrompt("\033[36m请输入StorageName: \033[0m")
storageName, err := rl.Readline()
if err != nil {
return err
}
rl.SetPrompt("\033[36m请输入Location: \033[0m")
location, err := rl.Readline()
if err != nil {
return err
}
rl.SetPrompt("\033[36m请输入RootDir: \033[0m")
rootDir, err := rl.Readline()
if err != nil {
return err
}
userSpace.Storage = &cortypes.LocalType{
Type: "Local",
Location: cortypes.Location{
StorageName: storageName,
Location: location,
},
}
userSpace.Credential = &cortypes.LocalCred{
Type: "Local",
RootDir: rootDir,
}
return nil
}
func (userSpace *MyUserSpace) collectObsConfig(rl *readline.Instance) error {
var err error
rl.SetPrompt("\033[36m请输入Region: \033[0m")
region, err := rl.Readline()
if err != nil {
return err
}
rl.SetPrompt("\033[36m请输入Endpoint: \033[0m")
endpoint, err := rl.Readline()
if err != nil {
return err
}
rl.SetPrompt("\033[36m请输入Bucket: \033[0m")
bucket, err := rl.Readline()
if err != nil {
return err
}
rl.SetPrompt("\033[36m请输入ProjectID: \033[0m")
projectID, err := rl.Readline()
if err != nil {
return err
}
rl.SetPrompt("\033[36m请输入AccessKeyID: \033[0m")
accessKey, err := rl.Readline()
if err != nil {
return err
}
secretBytes, err := rl.ReadPassword("\033[36m请输入AccessKeySecret: \033[0m")
if err != nil {
return err
}
secretKey := string(secretBytes)
userSpace.Storage = &cortypes.OBSType{
Type: "OBS",
Region: region,
Endpoint: endpoint,
Bucket: bucket,
ProjectID: projectID,
}
userSpace.Credential = &cortypes.OBSCred{
Type: "OBS",
AK: accessKey,
SK: secretKey,
}
for {
rl.SetPrompt("\033[36m是否支持存储服务间直传文件(y/n): \033[0m")
input, err := rl.Readline()
if err != nil {
return err
}
switch strings.ToLower(strings.TrimSpace(input)) {
case "y", "yes":
userSpace.Features = append(userSpace.Features, &cortypes.S2STransferFeature{
Type: "S2STransfer",
})
return nil
case "n", "no":
fmt.Println("\033[36m不支持存储服务间直传文件 \033[0m")
return nil
default:
fmt.Println("\033[31m无效输入请输入 y/n 或 yes/no \033[0m")
}
}
}
func (userSpace *MyUserSpace) collectOssConfig(rl *readline.Instance) error {
var err error
rl.SetPrompt("\033[36m请输入Region: \033[0m")
region, err := rl.Readline()
if err != nil {
return err
}
rl.SetPrompt("\033[36m请输入Endpoint: \033[0m")
endpoint, err := rl.Readline()
if err != nil {
return err
}
rl.SetPrompt("\033[36m请输入Bucket: \033[0m")
bucket, err := rl.Readline()
if err != nil {
return err
}
rl.SetPrompt("\033[36m请输入AccessKeyID: \033[0m")
accessKey, err := rl.Readline()
if err != nil {
return err
}
secretBytes, err := rl.ReadPassword("\033[36m请输入AccessKeySecret: \033[0m")
if err != nil {
return err
}
secretKey := string(secretBytes)
userSpace.Storage = &cortypes.OSSType{
Region: region,
Endpoint: endpoint,
Bucket: bucket,
}
userSpace.Credential = &cortypes.OSSCred{
Type: "OSS",
AK: accessKey,
SK: secretKey,
}
return nil
}
func (userSpace *MyUserSpace) collectCosConfig(rl *readline.Instance) error {
var err error
rl.SetPrompt("\033[36m请输入Region: \033[0m")
region, err := rl.Readline()
if err != nil {
return err
}
rl.SetPrompt("\033[36m请输入Endpoint: \033[0m")
endpoint, err := rl.Readline()
if err != nil {
return err
}
rl.SetPrompt("\033[36m请输入Bucket: \033[0m")
bucket, err := rl.Readline()
if err != nil {
return err
}
rl.SetPrompt("\033[36m请输入AccessKeyID: \033[0m")
accessKey, err := rl.Readline()
if err != nil {
return err
}
secretBytes, err := rl.ReadPassword("\033[36m请输入AccessKeySecret: \033[0m")
if err != nil {
return err
}
secretKey := string(secretBytes)
userSpace.Storage = &cortypes.COSType{
Type: "COS",
Region: region,
Endpoint: endpoint,
Bucket: bucket,
}
userSpace.Credential = &cortypes.COSCred{
Type: "COS",
AK: accessKey,
SK: secretKey,
}
return nil
}
func (userSpace *MyUserSpace) collectEfileConfig(rl *readline.Instance) error {
var err error
rl.SetPrompt("\033[36m请输入ClusterID: \033[0m")
clusterID, err := rl.Readline()
if err != nil {
return err
}
rl.SetPrompt("\033[36m请输入TokenURL: \033[0m")
tokenURL, err := rl.Readline()
if err != nil {
return err
}
rl.SetPrompt("\033[36m请输入APIURL: \033[0m")
apiURL, err := rl.Readline()
if err != nil {
return err
}
tokenExpire := 0
for {
rl.SetPrompt("\033[36m请输入TokenExpire: \033[0m")
valueInt, err := rl.Readline()
if err != nil {
return err
}
if strings.TrimSpace(valueInt) == "" {
fmt.Println("\033[31m错误输入不能为空请输入正整数\033[0m")
continue
}
num, err := strconv.ParseInt(valueInt, 10, 64)
if err != nil {
fmt.Printf("\033[31m错误'%s' 不是有效整数,请输入正整数\033[0m\n", valueInt)
continue
}
if num <= 0 {
fmt.Printf("\033[31m错误%d 不是正整数,请输入大于 0 的整数\033[0m\n", num)
continue
}
tokenExpire = int(num)
break
}
rl.SetPrompt("\033[36m请输入User: \033[0m")
user, err := rl.Readline()
if err != nil {
return err
}
passwordBytes, err := rl.ReadPassword("\033[36m请输入Password: \033[0m")
if err != nil {
return err
}
password := string(passwordBytes)
rl.SetPrompt("\033[36m请输入OrgID: \033[0m")
orgID, err := rl.Readline()
if err != nil {
return err
}
userSpace.Storage = &cortypes.EFileType{
Type: "EFile",
ClusterID: clusterID,
}
userSpace.Credential = &cortypes.EFileCred{
Type: "EFile",
TokenURL: tokenURL,
APIURL: apiURL,
TokenExpire: tokenExpire,
User: user,
Password: password,
OrgID: orgID,
}
for {
rl.SetPrompt("\033[36m是否提供能进行EC计算的接口(y/n): \033[0m")
input, err := rl.Readline()
if err != nil {
return err
}
switch strings.ToLower(strings.TrimSpace(input)) {
case "y", "yes":
userSpace.Features = append(userSpace.Features, &cortypes.ECMultiplierFeature{
Type: "ECMultiplier",
})
return nil
case "n", "no":
fmt.Println("\033[36m未提供能进行EC计算的接口 \033[0m")
return nil
default:
fmt.Println("\033[31m无效输入请输入 y/n 或 yes/no \033[0m")
}
}
}
func (userSpace *MyUserSpace) collectS3Config(rl *readline.Instance) error {
var err error
rl.SetPrompt("\033[36m请输入Region: \033[0m")
region, err := rl.Readline()
if err != nil {
return err
}
rl.SetPrompt("\033[36m请输入Endpoint: \033[0m")
endpoint, err := rl.Readline()
if err != nil {
return err
}
rl.SetPrompt("\033[36m请输入Bucket: \033[0m")
bucket, err := rl.Readline()
if err != nil {
return err
}
rl.SetPrompt("\033[36m请输入AccessKeyID: \033[0m")
accessKey, err := rl.Readline()
if err != nil {
return err
}
secretBytes, err := rl.ReadPassword("\033[36m请输入AccessKeySecret: \033[0m")
if err != nil {
return err
}
secretKey := string(secretBytes)
userSpace.Storage = &cortypes.S3Type{
Type: "S3",
Region: region,
Endpoint: endpoint,
Bucket: bucket,
}
userSpace.Credential = &cortypes.S3Cred{
Type: "S3",
AK: accessKey,
SK: secretKey,
}
for {
rl.SetPrompt("\033[36m是否支持分段上传(y/n): \033[0m")
input, err := rl.Readline()
if err != nil {
return err
}
switch strings.ToLower(strings.TrimSpace(input)) {
case "y", "yes":
userSpace.Features = append(userSpace.Features, &cortypes.MultipartUploadFeature{
Type: "MultipartUpload",
})
return nil
case "n", "no":
fmt.Println("\033[36m不支持分段上传 \033[0m")
return nil
default:
fmt.Println("\033[31m无效输入请输入 y/n 或 yes/no \033[0m")
}
}
}
func (userSpace *MyUserSpace) collectShardStore(rl *readline.Instance) error {
for {
rl.SetPrompt("\033[36m是否开启分片存储功能(y/n): \033[0m")
input, err := rl.Readline()
if err != nil {
return err
}
switch strings.ToLower(strings.TrimSpace(input)) {
case "y", "yes":
for {
rl.SetPrompt("\033[36m请输入最大Size: \033[0m")
sizeInput, err := rl.Readline()
if err != nil {
return err
}
if strings.TrimSpace(sizeInput) == "" {
fmt.Println("\033[31m错误输入不能为空31m错误输入不能为空请输入正整数\033[0m")
continue
}
maxSize, err := strconv.ParseInt(sizeInput, 10, 64)
if err != nil {
fmt.Printf("\033[31m错误'%s' 不是有效整数,请输入正整数\033[0m\n", sizeInput)
continue
}
if maxSize <= 0 {
fmt.Printf("\033[31m错误%d 不是正整数,请输入大于 0 的整数\033[0m\n", maxSize)
continue
}
userSpace.ShardStore = &cortypes.ShardStoreUserConfig{
MaxSize: maxSize,
}
return nil
}
case "n", "no":
fmt.Println("\033[31m分片存储未启用 \033[0m")
return nil
default:
fmt.Println("\033[31m无效输入请输入 y/n 或 yes/no \033[0m")
}
}
}
func (userSpace *MyUserSpace) collectWorkingDir(rl *readline.Instance) error {
for {
rl.SetPrompt("\033[36m默认工作路径(WorkingDir)为jcs是否修改(y/n): \033[0m")
input, err := rl.Readline()
if err != nil {
return err
}
switch strings.ToLower(strings.TrimSpace(input)) {
case "y", "yes":
rl.SetPrompt("\033[36m请输入新的工作路径(WorkingDir): \033[0m")
newValue, err := rl.Readline()
if err != nil {
return err
}
if newValue != "" {
userSpace.WorkingDir = newValue
}
return nil
case "n", "no":
userSpace.WorkingDir = "jcs"
return nil
default:
fmt.Println("\033[31m无效输入请输入 y/n 或 yes/no \033[0m")
}
}
}

View File

@ -0,0 +1,111 @@
package userspace
import (
"fmt"
"strconv"
"strings"
"github.com/chzyer/readline"
"github.com/spf13/cobra"
cliapi "gitlink.org.cn/cloudream/jcs-pub/client/sdk/api/v1"
clitypes "gitlink.org.cn/cloudream/jcs-pub/client/types"
"gitlink.org.cn/cloudream/jcs-pub/jcsctl/cmd"
)
func init() {
cmd := cobra.Command{
Use: "delete",
Short: "delete a cloud storage",
Run: func(c *cobra.Command, args []string) {
ctx := cmd.GetCmdCtx(c)
delete(c, ctx)
},
}
UserSpaceCmd.AddCommand(&cmd)
}
func delete(c *cobra.Command, ctx *cmd.CommandContext) {
rl, err := readline.New("> ")
if err != nil {
fmt.Printf("\033[31m初始化命令行失败: %v\033[0m\n", err)
return
}
defer rl.Close()
fmt.Printf("\033[1;36m请选择删除依据: \033[0m\n")
options := []string{"ID", "Name"}
for i, option := range options {
fmt.Printf("\033[33m%d. %s\033[0m\n", i+1, option)
}
rl.SetPrompt("\033[36m> \033[0m")
line, err := rl.Readline()
if err != nil {
return
}
var userSpace clitypes.UserSpace
trimmed := strings.TrimSpace(line)
switch trimmed {
case "1":
rl.SetPrompt("\033[36m请输入云存储ID(ID): \033[0m")
idInput, err := rl.Readline()
if err != nil || idInput == "" {
return
}
id, err := strconv.ParseInt(strings.TrimSpace(idInput), 10, 64)
if err != nil {
fmt.Printf("\033[31mID 格式错误: %v\033[0m\n", err)
return
}
resp, err := ctx.Client.UserSpaceGet(cliapi.UserSpaceGet{
UserSpaceID: clitypes.UserSpaceID(id),
})
if err != nil {
fmt.Printf("\033[31m保存配置失败: %v\033[0m", err)
return
}
userSpace = resp.UserSpace
case "2":
rl.SetPrompt("\033[36m请输入云存储名称(Name): \033[0m")
nameInput, err := rl.Readline()
if err != nil || nameInput == "" {
fmt.Println("\033[31m输入已取消\033[0m")
return
}
name := strings.TrimSpace(nameInput)
resp, err := ctx.Client.UserSpaceGetByName(cliapi.UserSpaceGetByName{
Name: name,
})
if err != nil {
fmt.Printf("\033[31m保存配置失败: %v\033[0m", err)
return
}
userSpace = resp.UserSpace
default:
fmt.Printf("\033[31m错误: 无效选项 '%s',请输入序号!\033[0m\n", line)
}
fmt.Println("\n\033[1;36m找到云存储:\033[0m")
fmt.Printf("\033[1;36mID:%d 名称:%s 类型:%s\033[0m\n", userSpace.UserSpaceID, userSpace.Name, userSpace.Storage.GetStorageType())
rl.SetPrompt("\033[31m确认删除(y/n): \033[0m")
confirm, err := rl.Readline()
if err != nil || strings.ToLower(strings.TrimSpace(confirm)) != "y" {
return
}
_, err = ctx.Client.UserSpaceDelete(cliapi.UserSpaceDelete{
UserSpaceID: userSpace.UserSpaceID,
})
if err != nil {
fmt.Printf("\033[31m删除失败: %v\033[0m\n", err)
return
}
fmt.Printf("\n\033[32m删除成功: %s\033[0m\n", userSpace.Name)
}

106
jcsctl/cmd/userspace/ls.go Normal file
View File

@ -0,0 +1,106 @@
package userspace
import (
"fmt"
"strconv"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/spf13/cobra"
cliapi "gitlink.org.cn/cloudream/jcs-pub/client/sdk/api/v1"
clitypes "gitlink.org.cn/cloudream/jcs-pub/client/types"
"gitlink.org.cn/cloudream/jcs-pub/jcsctl/cmd"
)
func init() {
var opt lsOpt
cmd := cobra.Command{
Use: "ls",
Args: cobra.MaximumNArgs(1),
Run: func(c *cobra.Command, args []string) {
ctx := cmd.GetCmdCtx(c)
ls(c, ctx, opt, args)
},
}
cmd.Flags().BoolVarP(&opt.ByID, "id", "", false, "按id查询")
cmd.Flags().BoolVarP(&opt.ShowPassword, "password", "p", false, "显示密码信息,请在安全环境下使用")
UserSpaceCmd.AddCommand(&cmd)
}
type lsOpt struct {
ByID bool
ShowPassword bool
}
func ls(c *cobra.Command, ctx *cmd.CommandContext, opt lsOpt, args []string) {
// 仅ls无参数
if len(args) == 0 {
resp, err := ctx.Client.UserSpaceGetAll()
if err != nil {
cmd.ErrorExitln(err.Error())
return
}
fmt.Printf("total: %d\n", len(resp.UserSpaces))
tb := table.NewWriter()
tb.AppendHeader(table.Row{"ID", "Name", "StorageType"})
for _, userSpace := range resp.UserSpaces {
tb.AppendRow(table.Row{userSpace.UserSpaceID, userSpace.Name, userSpace.Storage.GetStorageType()})
}
fmt.Println(tb.Render())
return
}
searchKey := args[0]
var userSpace *clitypes.UserSpace
if opt.ByID {
id, err := strconv.Atoi(searchKey)
if err != nil {
cmd.ErrorExitln("ID必须是数字")
return
}
result, err := ctx.Client.UserSpaceGet(cliapi.UserSpaceGet{
UserSpaceID: clitypes.UserSpaceID(id),
})
if err != nil {
cmd.ErrorExitln(err.Error())
return
}
userSpace = &result.UserSpace
} else {
result, err := ctx.Client.UserSpaceGetByName(cliapi.UserSpaceGetByName{
Name: searchKey,
})
if err != nil {
cmd.ErrorExitln(err.Error())
return
}
userSpace = &result.UserSpace
}
if userSpace == nil {
cmd.ErrorExitln(fmt.Sprintf("未找到匹配的云存储: %s", searchKey))
return
}
fmt.Println("\n\033[1;36m云存储详情\033[0m")
fmt.Println("----------------------------------")
fmt.Printf("\033[1m%-8s\033[0m %d\n", "ID:", userSpace.UserSpaceID)
fmt.Printf("\033[1m%-8s\033[0m %s\n", "名称:", userSpace.Name)
fmt.Printf("\033[1m%-8s\033[0m %s\n", "类型:", userSpace.Storage.GetStorageType())
fmt.Printf("\033[1m%-8s\033[0m %s\n", "Location:", userSpace.Storage.GetLocation().Location)
if opt.ShowPassword {
fmt.Printf("\033[1m%-8s\033[0m %s\n", "Credential:", userSpace.Credential.String(true))
} else {
fmt.Printf("\033[1m%-8s\033[0m %s\n", "Credential:", userSpace.Credential.String(false))
}
if len(userSpace.Features) > 0 {
fmt.Printf("\033[1m%-8s\033[0m %s\n", "Features:", userSpace.Features[0].GetFeatureType())
}
fmt.Printf("\033[1m%-8s\033[0m %s\n", "WorkingDir:", userSpace.WorkingDir)
fmt.Println("----------------------------------")
}

View File

@ -0,0 +1,321 @@
package userspace
import (
"fmt"
"strconv"
"strings"
"github.com/chzyer/readline"
"github.com/spf13/cobra"
"gitlink.org.cn/cloudream/jcs-pub/client/sdk/api/v1"
cliapi "gitlink.org.cn/cloudream/jcs-pub/client/sdk/api/v1"
clitypes "gitlink.org.cn/cloudream/jcs-pub/client/types"
cortypes "gitlink.org.cn/cloudream/jcs-pub/coordinator/types"
"gitlink.org.cn/cloudream/jcs-pub/jcsctl/cmd"
)
type UserSpaceUpdate struct {
api.UserSpaceUpdate
}
func init() {
cmd := cobra.Command{
Use: "update",
Short: "update a new cloud storage",
Run: func(c *cobra.Command, args []string) {
ctx := cmd.GetCmdCtx(c)
update(c, ctx)
},
}
UserSpaceCmd.AddCommand(&cmd)
}
func update(c *cobra.Command, ctx *cmd.CommandContext) {
rl, err := readline.New("> ")
if err != nil {
fmt.Printf("\033[31m初始化命令行失败: %v\033[0m\n", err)
return
}
defer rl.Close()
rl.SetPrompt("\033[36m请输入云存储ID(ID): \033[0m")
idInput, err := rl.Readline()
if err != nil || idInput == "" {
return
}
id, err := strconv.ParseInt(strings.TrimSpace(idInput), 10, 64)
if err != nil {
fmt.Printf("\033[31mID 格式错误: %v\033[0m\n", err)
return
}
resp, err := ctx.Client.UserSpaceGet(cliapi.UserSpaceGet{
UserSpaceID: clitypes.UserSpaceID(id),
})
if err != nil {
fmt.Printf("\033[31m云存储id=%d 不存在: %v\033[0m\n", id, err)
return
}
rl.SetPrompt("\033[36m请输入修改后的存储服务名称(Name): \033[0m")
name, err := rl.Readline()
if err != nil {
return
}
var userSpaceUpdate UserSpaceUpdate
userSpaceUpdate.UserSpaceID = clitypes.UserSpaceID(id)
userSpaceUpdate.Name = name
storageType := resp.UserSpace.Storage.GetStorageType()
switch storageType {
case "Local":
err = userSpaceUpdate.collectLocalConfig(rl)
case "OBS":
err = userSpaceUpdate.collectObsConfig(rl)
case "OSS":
err = userSpaceUpdate.collectOssConfig(rl)
case "COS":
err = userSpaceUpdate.collectCosConfig(rl)
case "EFile":
err = userSpaceUpdate.collectEfileConfig(rl)
case "S3":
err = userSpaceUpdate.collectS3Config(rl)
}
if err != nil {
return
}
_, err = ctx.Client.UserSpaceUpdate(userSpaceUpdate.UserSpaceUpdate)
if err != nil {
fmt.Printf("\033[31m更新配置失败: %v\033[0m", err)
return
}
fmt.Println("\033[32m配置更新成功!\033[0m")
}
func (userSpace *UserSpaceUpdate) collectLocalConfig(rl *readline.Instance) error {
rl.SetPrompt("\033[36m请输入RootDir: \033[0m")
rootDir, err := rl.Readline()
if err != nil {
return err
}
userSpace.Credential = &cortypes.LocalCred{
Type: "Local",
RootDir: rootDir,
}
return nil
}
func (userSpace *UserSpaceUpdate) collectObsConfig(rl *readline.Instance) error {
rl.SetPrompt("\033[36m请输入AccessKeyID: \033[0m")
accessKey, err := rl.Readline()
if err != nil {
return err
}
secretBytes, err := rl.ReadPassword("\033[36m请输入AccessKeySecret: \033[0m")
if err != nil {
return err
}
secretKey := string(secretBytes)
userSpace.Credential = &cortypes.OBSCred{
Type: "OBS",
AK: accessKey,
SK: secretKey,
}
for {
rl.SetPrompt("\033[36m是否支持存储服务间直传文件(y/n): \033[0m")
input, err := rl.Readline()
if err != nil {
return err
}
switch strings.ToLower(strings.TrimSpace(input)) {
case "y", "yes":
userSpace.Features = append(userSpace.Features, &cortypes.S2STransferFeature{
Type: "S2STransfer",
})
return nil
case "n", "no":
userSpace.Features = nil
return nil
default:
fmt.Println("\033[31m无效输入请输入 y/n 或 yes/no \033[0m")
}
}
}
func (userSpace *UserSpaceUpdate) collectOssConfig(rl *readline.Instance) error {
rl.SetPrompt("\033[36m请输入AccessKeyID: \033[0m")
accessKey, err := rl.Readline()
if err != nil {
return err
}
secretBytes, err := rl.ReadPassword("\033[36m请输入AccessKeySecret: \033[0m")
if err != nil {
return err
}
secretKey := string(secretBytes)
userSpace.Credential = &cortypes.OSSCred{
Type: "OSS",
AK: accessKey,
SK: secretKey,
}
return nil
}
func (userSpace *UserSpaceUpdate) collectCosConfig(rl *readline.Instance) error {
rl.SetPrompt("\033[36m请输入AccessKeyID: \033[0m")
accessKey, err := rl.Readline()
if err != nil {
return err
}
secretBytes, err := rl.ReadPassword("\033[36m请输入AccessKeySecret: \033[0m")
if err != nil {
return err
}
secretKey := string(secretBytes)
userSpace.Credential = &cortypes.COSCred{
Type: "COS",
AK: accessKey,
SK: secretKey,
}
return nil
}
func (userSpace *UserSpaceUpdate) collectEfileConfig(rl *readline.Instance) error {
rl.SetPrompt("\033[36m请输入TokenURL: \033[0m")
tokenURL, err := rl.Readline()
if err != nil {
return err
}
rl.SetPrompt("\033[36m请输入APIURL: \033[0m")
apiURL, err := rl.Readline()
if err != nil {
return err
}
tokenExpire := 0
for {
rl.SetPrompt("\033[36m请输入TokenExpire: \033[0m")
valueInt, err := rl.Readline()
if err != nil {
return err
}
if strings.TrimSpace(valueInt) == "" {
fmt.Println("\033[31m错误输入不能为空请输入正整数\033[0m")
continue
}
num, err := strconv.ParseInt(valueInt, 10, 64)
if err != nil {
fmt.Printf("\033[31m错误'%s' 不是有效整数,请输入正整数\033[0m\n", valueInt)
continue
}
if num <= 0 {
fmt.Printf("\033[31m错误%d 不是正整数,请输入大于 0 的整数\033[0m\n", num)
continue
}
tokenExpire = int(num)
break
}
rl.SetPrompt("\033[36m请输入User: \033[0m")
user, err := rl.Readline()
if err != nil {
return err
}
passwordBytes, err := rl.ReadPassword("\033[36m请输入Password: \033[0m")
if err != nil {
return err
}
password := string(passwordBytes)
rl.SetPrompt("\033[36m请输入OrgID: \033[0m")
orgID, err := rl.Readline()
if err != nil {
return err
}
userSpace.Credential = &cortypes.EFileCred{
Type: "EFile",
TokenURL: tokenURL,
APIURL: apiURL,
TokenExpire: tokenExpire,
User: user,
Password: password,
OrgID: orgID,
}
for {
rl.SetPrompt("\033[36m是否提供能进行EC计算的接口(y/n): \033[0m")
input, err := rl.Readline()
if err != nil {
return err
}
switch strings.ToLower(strings.TrimSpace(input)) {
case "y", "yes":
userSpace.Features = append(userSpace.Features, &cortypes.ECMultiplierFeature{
Type: "ECMultiplier",
})
return nil
case "n", "no":
userSpace.Features = nil
return nil
default:
fmt.Println("\033[31m无效输入请输入 y/n 或 yes/no \033[0m")
}
}
}
func (userSpace *UserSpaceUpdate) collectS3Config(rl *readline.Instance) error {
rl.SetPrompt("\033[36m请输入AccessKeyID: \033[0m")
accessKey, err := rl.Readline()
if err != nil {
return err
}
secretBytes, err := rl.ReadPassword("\033[36m请输入AccessKeySecret: \033[0m")
if err != nil {
return err
}
secretKey := string(secretBytes)
userSpace.Credential = &cortypes.S3Cred{
Type: "S3",
AK: accessKey,
SK: secretKey,
}
for {
rl.SetPrompt("\033[36m是否支持分段上传(y/n): \033[0m")
input, err := rl.Readline()
if err != nil {
return err
}
switch strings.ToLower(strings.TrimSpace(input)) {
case "y", "yes":
userSpace.Features = append(userSpace.Features, &cortypes.MultipartUploadFeature{
Type: "MultipartUpload",
})
return nil
case "n", "no":
userSpace.Features = nil
return nil
default:
fmt.Println("\033[31m无效输入请输入 y/n 或 yes/no \033[0m")
}
}
}

View File

@ -0,0 +1,14 @@
package userspace
import (
"github.com/spf13/cobra"
"gitlink.org.cn/cloudream/jcs-pub/jcsctl/cmd"
)
var UserSpaceCmd = &cobra.Command{
Use: "userspace",
}
func init() {
cmd.RootCmd.AddCommand(UserSpaceCmd)
}