Browse Source

客户端鉴权改为证书

feature_gxh
Sydonian 5 months ago
parent
commit
15a9bb1642
21 changed files with 845 additions and 259 deletions
  1. +219
    -0
      client/internal/cmdline/cert.go
  2. +11
    -4
      client/internal/cmdline/serve.go
  3. +11
    -4
      client/internal/cmdline/vfstest.go
  4. +1
    -1
      client/internal/config/config.go
  5. +80
    -0
      client/internal/http/auth/auth.go
  6. +1
    -1
      client/internal/http/http.go
  7. +13
    -6
      client/internal/http/server.go
  8. +104
    -7
      client/internal/http/types/config.go
  9. +1
    -0
      client/internal/http/types/types.go
  10. +5
    -5
      client/internal/http/types/utils.go
  11. +26
    -24
      client/internal/http/v1/aws_auth.go
  12. +14
    -13
      client/internal/http/v1/bucket.go
  13. +6
    -5
      client/internal/http/v1/mount.go
  14. +57
    -56
      client/internal/http/v1/object.go
  15. +28
    -27
      client/internal/http/v1/package.go
  16. +25
    -24
      client/internal/http/v1/presigned.go
  17. +47
    -45
      client/internal/http/v1/server.go
  18. +29
    -28
      client/internal/http/v1/user_space.go
  19. +150
    -0
      client/sdk/signer/signer.go
  20. +1
    -0
      common/pkgs/storage/obs/obs.go
  21. +16
    -9
      coordinator/internal/cmd/cert.go

+ 219
- 0
client/internal/cmdline/cert.go View File

@@ -0,0 +1,219 @@
package cmdline

import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"fmt"
"math/big"
"os"
"path/filepath"
"time"

"github.com/spf13/cobra"
"gitlink.org.cn/cloudream/jcs-pub/client/internal/http/auth"
)

func init() {
certCmd := cobra.Command{
Use: "cert",
}
RootCmd.AddCommand(&certCmd)

certRoot := cobra.Command{
Use: "root [outputDir]",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
certRoot(args[0])
},
}
certCmd.AddCommand(&certRoot)

var certFilePath string
var keyFilePath string

certServer := cobra.Command{
Use: "server [outputDir]",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
certServer(certFilePath, keyFilePath, args[0])
},
}
certServer.Flags().StringVar(&certFilePath, "cert", "", "CA certificate file path")
certServer.Flags().StringVar(&keyFilePath, "key", "", "CA key file path")
certCmd.AddCommand(&certServer)

certClient := cobra.Command{
Use: "client [outputDir]",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
certClient(certFilePath, keyFilePath, args[0])
},
}
certClient.Flags().StringVar(&certFilePath, "cert", "", "CA certificate file path")
certClient.Flags().StringVar(&keyFilePath, "key", "", "CA key file path")
certCmd.AddCommand(&certClient)
}

func certRoot(output string) {
caPriv, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)

// 创建 CA 证书模板
caTemplate := &x509.Certificate{
SerialNumber: big.NewInt(1),
Subject: pkix.Name{
Organization: []string{"JCS"},
},
NotBefore: time.Now(),
NotAfter: time.Now().AddDate(10, 0, 0), // 有效期10年
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageDigitalSignature,
BasicConstraintsValid: true,
IsCA: true,
}

// 自签名 CA 证书
caCertDER, _ := x509.CreateCertificate(rand.Reader, caTemplate, caTemplate, &caPriv.PublicKey, caPriv)

// 保存 CA 证书和私钥
writePem(filepath.Join(output, "ca_cert.pem"), "CERTIFICATE", caCertDER)

privDER, _ := x509.MarshalECPrivateKey(caPriv)
writePem(filepath.Join(output, "ca_key.pem"), "EC PRIVATE KEY", privDER)
fmt.Println("CA certificate and key saved to", output)
}

func certServer(certFile string, keyFile string, output string) {
// 读取 CA 证书和私钥
caCertPEM, err := os.ReadFile(certFile)
if err != nil {
fmt.Println("Failed to read CA certificate:", err)
return
}
caKeyPEM, err := os.ReadFile(keyFile)
if err != nil {
fmt.Println("Failed to read CA key:", err)
return
}
caCertPEMBlock, _ := pem.Decode(caCertPEM)
if caCertPEMBlock == nil {
fmt.Println("Failed to decode CA certificate")
return
}
caKeyPEMBlock, _ := pem.Decode(caKeyPEM)
if caKeyPEMBlock == nil {
fmt.Println("Failed to decode CA key")
return
}

caCert, err := x509.ParseCertificate(caCertPEMBlock.Bytes)
if err != nil {
fmt.Println("Failed to parse CA certificate:", err)
return
}

caKey, err := x509.ParseECPrivateKey(caKeyPEMBlock.Bytes)
if err != nil {
fmt.Println("Failed to parse CA key:", err)
return
}

// 生成服务端私钥
serverPriv, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)

// 服务端证书模板
serverTemplate := &x509.Certificate{
SerialNumber: big.NewInt(2),
Subject: pkix.Name{
CommonName: "localhost",
},
NotBefore: time.Now(),
NotAfter: time.Now().AddDate(1, 0, 0), // 有效期1年
KeyUsage: x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
}

// 添加主机名/IP 到证书
serverTemplate.DNSNames = []string{auth.ClientInternalSNI}

// 用 CA 签发服务端证书
serverCertDER, _ := x509.CreateCertificate(rand.Reader, serverTemplate, caCert, &serverPriv.PublicKey, caKey)

// 保存服务端证书和私钥
writePem(filepath.Join(output, "server_cert.pem"), "CERTIFICATE", serverCertDER)

privPem, _ := x509.MarshalECPrivateKey(serverPriv)
writePem(filepath.Join(output, "server_key.pem"), "EC PRIVATE KEY", privPem)
fmt.Println("Server certificate and key saved to", output)
}

func certClient(certFile string, keyFile string, output string) {
// 读取 CA 证书和私钥
caCertPEM, err := os.ReadFile(certFile)
if err != nil {
fmt.Println("Failed to read CA certificate:", err)
return
}
caKeyPEM, err := os.ReadFile(keyFile)
if err != nil {
fmt.Println("Failed to read CA key:", err)
return
}
caCertPEMBlock, _ := pem.Decode(caCertPEM)
if caCertPEMBlock == nil {
fmt.Println("Failed to decode CA certificate")
return
}
caKeyPEMBlock, _ := pem.Decode(caKeyPEM)
if caKeyPEMBlock == nil {
fmt.Println("Failed to decode CA key")
return
}

caCert, err := x509.ParseCertificate(caCertPEMBlock.Bytes)
if err != nil {
fmt.Println("Failed to parse CA certificate:", err)
return
}

caKey, err := x509.ParseECPrivateKey(caKeyPEMBlock.Bytes)
if err != nil {
fmt.Println("Failed to parse CA key:", err)
return
}

// 生成客户端私钥
clientPriv, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)

// 客户端证书模板
clientTemplate := &x509.Certificate{
SerialNumber: big.NewInt(3),
Subject: pkix.Name{
CommonName: "client",
},
NotBefore: time.Now(),
NotAfter: time.Now().AddDate(1, 0, 0), // 有效期1年
KeyUsage: x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
BasicConstraintsValid: true,
}

// 用 CA 签发客户端证书
clientCertDER, _ := x509.CreateCertificate(rand.Reader, clientTemplate, caCert, &clientPriv.PublicKey, caKey)

// 保存客户端证书和私钥
writePem(filepath.Join(output, "client_cert.pem"), "CERTIFICATE", clientCertDER)

privPem, _ := x509.MarshalECPrivateKey(clientPriv)
writePem(filepath.Join(output, "client_key.pem"), "EC PRIVATE KEY", privPem)
fmt.Println("Client certificate and key saved to", output)
}

func writePem(filename, pemType string, bytes []byte) {
f, _ := os.Create(filename)
pem.Encode(f, &pem.Block{Type: pemType, Bytes: bytes})
f.Close()
}

+ 11
- 4
client/internal/cmdline/serve.go View File

@@ -192,13 +192,20 @@ func serveHTTP(configPath string, opts serveHTTPOptions) {
svc := services.NewService(publock, dlder, acStat, uploader, strgSel, stgMeta, db, evtPub, mnt, stgPool) svc := services.NewService(publock, dlder, acStat, uploader, strgSel, stgMeta, db, evtPub, mnt, stgPool)


// HTTP接口 // HTTP接口
httpCfg := config.Cfg().HTTP
if !opts.DisableHTTP && httpCfg != nil && httpCfg.Enabled {
httpCfgJSON := config.Cfg().HTTP
if !opts.DisableHTTP && httpCfgJSON != nil && httpCfgJSON.Enabled {
if opts.HTTPListenAddr != "" { if opts.HTTPListenAddr != "" {
httpCfg.Listen = opts.HTTPListenAddr
httpCfgJSON.Listen = opts.HTTPListenAddr
} }
} else { } else {
httpCfg = nil
httpCfgJSON = &http.ConfigJSON{
Enabled: false,
}
}
httpCfg, err := httpCfgJSON.Build()
if err != nil {
logger.Errorf("build http config: %v", err)
os.Exit(1)
} }
httpSvr := http.NewServer(httpCfg, svc) httpSvr := http.NewServer(httpCfg, svc)
httpChan := httpSvr.Start() httpChan := httpSvr.Start()


+ 11
- 4
client/internal/cmdline/vfstest.go View File

@@ -174,13 +174,20 @@ func vfsTest(configPath string, opts serveHTTPOptions) {
svc := services.NewService(publock, dlder, acStat, uploader, strgSel, stgMeta, db, evtPub, mnt, stgPool) svc := services.NewService(publock, dlder, acStat, uploader, strgSel, stgMeta, db, evtPub, mnt, stgPool)


// HTTP接口 // HTTP接口
httpCfg := config.Cfg().HTTP
if !opts.DisableHTTP && httpCfg != nil && httpCfg.Enabled {
httpCfgJSON := config.Cfg().HTTP
if !opts.DisableHTTP && httpCfgJSON != nil && httpCfgJSON.Enabled {
if opts.HTTPListenAddr != "" { if opts.HTTPListenAddr != "" {
httpCfg.Listen = opts.HTTPListenAddr
httpCfgJSON.Listen = opts.HTTPListenAddr
} }
} else { } else {
httpCfg = nil
httpCfgJSON = &http.ConfigJSON{
Enabled: false,
}
}
httpCfg, err := httpCfgJSON.Build()
if err != nil {
logger.Errorf("build http config: %v", err)
os.Exit(1)
} }
httpSvr := http.NewServer(httpCfg, svc) httpSvr := http.NewServer(httpCfg, svc)
httpChan := httpSvr.Start() httpChan := httpSvr.Start()


+ 1
- 1
client/internal/config/config.go View File

@@ -28,7 +28,7 @@ type Config struct {
Downloader downloader.Config `json:"downloader"` Downloader downloader.Config `json:"downloader"`
DownloadStrategy strategy.Config `json:"downloadStrategy"` DownloadStrategy strategy.Config `json:"downloadStrategy"`
TickTock ticktock.Config `json:"tickTock"` TickTock ticktock.Config `json:"tickTock"`
HTTP *http.Config `json:"http"`
HTTP *http.ConfigJSON `json:"http"`
Mount *mntcfg.Config `json:"mount"` Mount *mntcfg.Config `json:"mount"`
AccessToken *accesstoken.Config `json:"accessToken"` AccessToken *accesstoken.Config `json:"accessToken"`
} }


+ 80
- 0
client/internal/http/auth/auth.go View File

@@ -0,0 +1,80 @@
package auth

import (
"crypto/tls"

"github.com/gin-gonic/gin"
"gitlink.org.cn/cloudream/common/pkgs/logger"
"gitlink.org.cn/cloudream/jcs-pub/client/internal/http/types"
"gitlink.org.cn/cloudream/jcs-pub/client/sdk/signer"
"gitlink.org.cn/cloudream/jcs-pub/common/ecode"
)

const (
ClientInternalSNI = "client.jcs-pub.internal"
)

type Auth struct {
cfg *types.Config
}

func New(cfg *types.Config) *Auth {
return &Auth{
cfg: cfg,
}
}

func (a *Auth) TLSConfigSelector(hello *tls.ClientHelloInfo) (*tls.Config, error) {
switch hello.ServerName {
case ClientInternalSNI:
return &tls.Config{
Certificates: []tls.Certificate{a.cfg.ServerCert},
ClientAuth: tls.RequireAndVerifyClientCert,
ClientCAs: a.cfg.RootCA,
NextProtos: []string{"h2", "http/1.1"},
}, nil

default:
return &tls.Config{
Certificates: []tls.Certificate{a.cfg.ServerCert},
ClientAuth: tls.NoClientCert,
NextProtos: []string{"h2", "http/1.1"},
}, nil
}
}

func (a *Auth) RejectNoCertAuth(c *gin.Context) {
if c.Request.TLS == nil || c.Request.TLS.ServerName != ClientInternalSNI {
c.AbortWithStatusJSON(401, types.Failed(ecode.Unauthorized, "must provide client certificate"))
return
}

c.Next()
}

func (a *Auth) Presigned(c *gin.Context) {
log := logger.WithField("HTTP", "Auth")

accID := signer.GetAccessKeyID(c.Request.URL)
if accID == "" {
log.Warn("access key id not found in query string")
c.AbortWithStatusJSON(401, types.Failed(ecode.Unauthorized, "access key id not found in query string"))
return
}

cliCert := a.cfg.ClientCerts[accID]
if cliCert == nil {
log.Warnf("client cert not found for access key id %s", accID)
c.AbortWithStatusJSON(401, types.Failed(ecode.Unauthorized, "client cert not found for access key id %s", accID))
return
}

err := signer.VerifyPresigned(cliCert.VerifyKey, c.Request.Method, c.Request.URL)
if err != nil {
log.Warn(err.Error())
c.AbortWithStatusJSON(401, types.Failed(ecode.Unauthorized, err.Error()))
return
}

c.Next()
}

+ 1
- 1
client/internal/http/http.go View File

@@ -2,4 +2,4 @@ package http


import "gitlink.org.cn/cloudream/jcs-pub/client/internal/http/types" import "gitlink.org.cn/cloudream/jcs-pub/client/internal/http/types"


type Config = types.Config
type ConfigJSON = types.ConfigJSON

+ 13
- 6
client/internal/http/server.go View File

@@ -2,11 +2,13 @@ package http


import ( import (
"context" "context"
"crypto/tls"
"net/http" "net/http"


"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"gitlink.org.cn/cloudream/common/pkgs/async" "gitlink.org.cn/cloudream/common/pkgs/async"
"gitlink.org.cn/cloudream/common/pkgs/logger" "gitlink.org.cn/cloudream/common/pkgs/logger"
"gitlink.org.cn/cloudream/jcs-pub/client/internal/http/auth"
"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"
@@ -24,25 +26,26 @@ type ExitEvent struct {
} }


type Server struct { type Server struct {
cfg *types.Config
cfg types.Config
httpSrv *http.Server httpSrv *http.Server
svc *services.Service svc *services.Service
eventChan *ServerEventChan eventChan *ServerEventChan
auth *auth.Auth
v1Svr *v1.Server v1Svr *v1.Server
} }


func NewServer(cfg *types.Config, svc *services.Service) *Server {
func NewServer(cfg types.Config, svc *services.Service) *Server {
return &Server{ return &Server{
cfg: cfg, cfg: cfg,
svc: svc, svc: svc,
eventChan: async.NewUnboundChannel[ServerEvent](), eventChan: async.NewUnboundChannel[ServerEvent](),
v1Svr: v1.NewServer(cfg, svc),
v1Svr: v1.NewServer(&cfg, svc),
} }
} }


func (s *Server) Start() *ServerEventChan { func (s *Server) Start() *ServerEventChan {
go func() { go func() {
if s.cfg == nil {
if !s.cfg.Enabled {
return return
} }


@@ -51,12 +54,16 @@ func (s *Server) Start() *ServerEventChan {
Addr: s.cfg.Listen, Addr: s.cfg.Listen,
Handler: engine, Handler: engine,
} }
s.auth = auth.New(&s.cfg)
s.httpSrv.TLSConfig = &tls.Config{
GetConfigForClient: s.auth.TLSConfigSelector,
}


s.v1Svr.InitRouters(engine.Group("/v1"))
s.v1Svr.InitRouters(engine.Group("/v1"), s.auth)


logger.Infof("start serving http at: %s", s.cfg.Listen) logger.Infof("start serving http at: %s", s.cfg.Listen)


err := s.httpSrv.ListenAndServe()
err := s.httpSrv.ListenAndServeTLS("", "")
s.eventChan.Send(ExitEvent{Err: err}) s.eventChan.Send(ExitEvent{Err: err})
}() }()
return s.eventChan return s.eventChan


+ 104
- 7
client/internal/http/types/config.go View File

@@ -1,12 +1,109 @@
package types package types


import "gitlink.org.cn/cloudream/jcs-pub/client/types"
import (
"crypto/ecdsa"
"crypto/sha256"
"crypto/tls"
"crypto/x509"
"encoding/hex"
"encoding/pem"
"fmt"
"os"

"gitlink.org.cn/cloudream/jcs-pub/client/sdk/signer"
"gitlink.org.cn/cloudream/jcs-pub/client/types"
)

type ConfigJSON struct {
Enabled bool `json:"enabled"`
Listen string `json:"listen"`
RootCA string `json:"rootCA"`
ServerCert string `json:"serverCert"`
ServerKey string `json:"serverKey"`
// 可信的客户端证书列表,在进行预签名接口中会用到。
ClientCerts []string `json:"clientCerts"`
MaxBodySize int64 `json:"maxBodySize"`
UserSpaceID types.UserSpaceID `json:"userSpaceID"` // TODO 进行访问量统计时,当前客户端所属的存储ID。临时解决方案。
}

func (c *ConfigJSON) Build() (Config, error) {
if !c.Enabled {
return Config{
Enabled: false,
}, nil
}

rootCAPool := x509.NewCertPool()

rootCAPem, err := os.ReadFile(c.RootCA)
if err != nil {
return Config{}, fmt.Errorf("reading root CA: %w", err)
}

if !rootCAPool.AppendCertsFromPEM(rootCAPem) {
return Config{}, fmt.Errorf("parsing root CA failed")
}

svrCert, err := tls.LoadX509KeyPair(c.ServerCert, c.ServerKey)
if err != nil {
return Config{}, fmt.Errorf("loading server cert: %w", err)
}

clientCerts := make(map[string]*ClientCert)
for _, p := range c.ClientCerts {
certPEM, err := os.ReadFile(p)
if err != nil {
return Config{}, fmt.Errorf("reading client cert %v: %w", p, err)
}

b, _ := pem.Decode(certPEM)
if len(b.Bytes) == 0 {
return Config{}, fmt.Errorf("decode client cert %v failed", p)
}
if b.Type != "CERTIFICATE" {
return Config{}, fmt.Errorf("invalid client cert %v: not a certificate", p)
}

cert, err := x509.ParseCertificate(b.Bytes)
if err != nil {
return Config{}, fmt.Errorf("parsing client cert %v: %w", p, err)
}

pubKey, ok := cert.PublicKey.(*ecdsa.PublicKey)
if !ok {
return Config{}, fmt.Errorf("invalid client cert %v: not an ECDSA public key", p)
}

pubKeyDer, _ := x509.MarshalPKIXPublicKey(pubKey)
pubHash := sha256.Sum256(pubKeyDer)
clientCerts[hex.EncodeToString(pubHash[:])] = &ClientCert{
Cert: cert,
VerifyKey: signer.NewVerifyKey(pubKey),
}
}

return Config{
Enabled: c.Enabled,
Listen: c.Listen,
RootCA: rootCAPool,
ServerCert: svrCert,
ClientCerts: clientCerts,
MaxBodySize: c.MaxBodySize,
UserSpaceID: c.UserSpaceID,
}, nil
}


type Config struct { type Config struct {
Enabled bool `json:"enabled"`
Listen string `json:"listen"`
AuthAccessKey string `json:"authAccessKey"` // TODO 临时办法
AuthSecretKey string `json:"authSecretKey"`
MaxBodySize int64 `json:"maxBodySize"`
UserSpaceID types.UserSpaceID `json:"userSpaceID"` // TODO 进行访问量统计时,当前客户端所属的存储ID。临时解决方案。
Enabled bool
Listen string
RootCA *x509.CertPool
ServerCert tls.Certificate
ClientCerts map[string]*ClientCert
MaxBodySize int64
UserSpaceID types.UserSpaceID
}

type ClientCert struct {
Cert *x509.Certificate
VerifyKey *signer.VerifyKey
} }

+ 1
- 0
client/internal/http/types/types.go View File

@@ -0,0 +1 @@
package types

client/internal/http/v1/utils.go → client/internal/http/types/utils.go View File

@@ -1,12 +1,12 @@
package http
package types


import ( import (
"fmt" "fmt"


"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"gitlink.org.cn/cloudream/common/consts/errorcode" "gitlink.org.cn/cloudream/common/consts/errorcode"
"gitlink.org.cn/cloudream/common/pkgs/mq"
"gitlink.org.cn/cloudream/common/utils/serder" "gitlink.org.cn/cloudream/common/utils/serder"
"gitlink.org.cn/cloudream/jcs-pub/common/ecode"
) )


type Response struct { type Response struct {
@@ -23,15 +23,15 @@ func OK(data any) Response {
} }
} }


func Failed(code string, format string, args ...any) Response {
func Failed(code ecode.ErrorCode, format string, args ...any) Response {
return Response{ return Response{
Code: code,
Code: string(code),
Message: fmt.Sprintf(format, args...), Message: fmt.Sprintf(format, args...),
} }
} }


func FailedError(err error) Response { func FailedError(err error) Response {
if codeErr, ok := err.(*mq.CodeMessageError); ok {
if codeErr, ok := err.(*ecode.CodeError); ok {
return Failed(codeErr.Code, codeErr.Message) return Failed(codeErr.Code, codeErr.Message)
} }



+ 26
- 24
client/internal/http/v1/aws_auth.go View File

@@ -1,5 +1,6 @@
package http package http


/*
import ( import (
"bytes" "bytes"
"context" "context"
@@ -28,12 +29,12 @@ const (
) )


type AWSAuth struct { type AWSAuth struct {
cfg *types.Config
cfg *types.ConfigJSON
cred aws.Credentials cred aws.Credentials
signer *v4.Signer signer *v4.Signer
} }


func NewAWSAuth(cfg *types.Config) *AWSAuth {
func NewAWSAuth(cfg *types.ConfigJSON) *AWSAuth {
auth := &AWSAuth{ auth := &AWSAuth{
cfg: cfg, cfg: cfg,
} }
@@ -56,13 +57,13 @@ func (a *AWSAuth) Auth(c *gin.Context) {


authorizationHeader := c.GetHeader(AuthorizationHeader) authorizationHeader := c.GetHeader(AuthorizationHeader)
if authorizationHeader == "" { if authorizationHeader == "" {
c.AbortWithStatusJSON(http.StatusBadRequest, Failed(errorcode.Unauthorized, "authorization header is missing"))
c.AbortWithStatusJSON(http.StatusBadRequest, types.Failed(ecode.Unauthorized, "authorization header is missing"))
return return
} }


_, headers, reqSig, err := parseAuthorizationHeader(authorizationHeader) _, headers, reqSig, err := parseAuthorizationHeader(authorizationHeader)
if err != nil { if err != nil {
c.AbortWithStatusJSON(http.StatusBadRequest, Failed(errorcode.Unauthorized, "invalid Authorization header format"))
c.AbortWithStatusJSON(http.StatusBadRequest, types.Failed(ecode.Unauthorized, "invalid Authorization header format"))
return return
} }


@@ -70,18 +71,18 @@ func (a *AWSAuth) Auth(c *gin.Context) {
rd := io.LimitReader(c.Request.Body, a.cfg.MaxBodySize) rd := io.LimitReader(c.Request.Body, a.cfg.MaxBodySize)
body, err := io.ReadAll(rd) body, err := io.ReadAll(rd)
if err != nil { if err != nil {
c.AbortWithStatusJSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "read request body failed"))
c.AbortWithStatusJSON(http.StatusBadRequest, types.Failed(ecode.BadArgument, "read request body failed"))
return return
} }


timestamp, err := time.Parse("20060102T150405Z", c.GetHeader("X-Amz-Date")) timestamp, err := time.Parse("20060102T150405Z", c.GetHeader("X-Amz-Date"))
if err != nil { if err != nil {
c.AbortWithStatusJSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "invalid X-Amz-Date header format"))
c.AbortWithStatusJSON(http.StatusBadRequest, types.Failed(ecode.BadArgument, "invalid X-Amz-Date header format"))
return return
} }


if time.Now().After(timestamp.Add(5 * time.Minute)) { if time.Now().After(timestamp.Add(5 * time.Minute)) {
c.AbortWithStatusJSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "X-Amz-Date is expired"))
c.AbortWithStatusJSON(http.StatusBadRequest, types.Failed(ecode.BadArgument, "X-Amz-Date is expired"))
return return
} }


@@ -91,7 +92,7 @@ func (a *AWSAuth) Auth(c *gin.Context) {
// 构造验签用的请求 // 构造验签用的请求
verifyReq, err := http.NewRequest(c.Request.Method, c.Request.URL.String(), nil) verifyReq, err := http.NewRequest(c.Request.Method, c.Request.URL.String(), nil)
if err != nil { if err != nil {
c.AbortWithStatusJSON(http.StatusOK, Failed(errorcode.OperationFailed, err.Error()))
c.AbortWithStatusJSON(http.StatusOK, types.Failed(ecode.OperationFailed, err.Error()))
return return
} }
for _, h := range headers { for _, h := range headers {
@@ -108,14 +109,14 @@ func (a *AWSAuth) Auth(c *gin.Context) {
err = signer.SignHTTP(context.TODO(), a.cred, verifyReq, hexPayloadHash, AuthService, AuthRegion, timestamp) err = signer.SignHTTP(context.TODO(), a.cred, verifyReq, hexPayloadHash, AuthService, AuthRegion, timestamp)
if err != nil { if err != nil {
logger.Warnf("sign request: %v", err) logger.Warnf("sign request: %v", err)
c.AbortWithStatusJSON(http.StatusOK, Failed(errorcode.OperationFailed, "sign request failed"))
c.AbortWithStatusJSON(http.StatusOK, types.Failed(ecode.OperationFailed, "sign request failed"))
return return
} }


verifySig := getSignatureFromAWSHeader(verifyReq) verifySig := getSignatureFromAWSHeader(verifyReq)
if !strings.EqualFold(verifySig, reqSig) { if !strings.EqualFold(verifySig, reqSig) {
logger.Warnf("signature mismatch, input header: %s, verify: %s", authorizationHeader, verifyReq.Header.Get(AuthorizationHeader)) logger.Warnf("signature mismatch, input header: %s, verify: %s", authorizationHeader, verifyReq.Header.Get(AuthorizationHeader))
c.AbortWithStatusJSON(http.StatusOK, Failed(errorcode.Unauthorized, "signature mismatch"))
c.AbortWithStatusJSON(http.StatusOK, types.Failed(ecode.Unauthorized, "signature mismatch"))
return return
} }


@@ -132,31 +133,31 @@ func (a *AWSAuth) AuthWithoutBody(c *gin.Context) {


authorizationHeader := c.GetHeader(AuthorizationHeader) authorizationHeader := c.GetHeader(AuthorizationHeader)
if authorizationHeader == "" { if authorizationHeader == "" {
c.AbortWithStatusJSON(http.StatusBadRequest, Failed(errorcode.Unauthorized, "authorization header is missing"))
c.AbortWithStatusJSON(http.StatusBadRequest, types.Failed(ecode.Unauthorized, "authorization header is missing"))
return return
} }


_, headers, reqSig, err := parseAuthorizationHeader(authorizationHeader) _, headers, reqSig, err := parseAuthorizationHeader(authorizationHeader)
if err != nil { if err != nil {
c.AbortWithStatusJSON(http.StatusBadRequest, Failed(errorcode.Unauthorized, "invalid Authorization header format"))
c.AbortWithStatusJSON(http.StatusBadRequest, types.Failed(ecode.Unauthorized, "invalid Authorization header format"))
return return
} }


timestamp, err := time.Parse("20060102T150405Z", c.GetHeader("X-Amz-Date")) timestamp, err := time.Parse("20060102T150405Z", c.GetHeader("X-Amz-Date"))
if err != nil { if err != nil {
c.AbortWithStatusJSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "invalid X-Amz-Date header format"))
c.AbortWithStatusJSON(http.StatusBadRequest, types.Failed(ecode.BadArgument, "invalid X-Amz-Date header format"))
return return
} }


if time.Now().After(timestamp.Add(5 * time.Minute)) { if time.Now().After(timestamp.Add(5 * time.Minute)) {
c.AbortWithStatusJSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "X-Amz-Date is expired"))
c.AbortWithStatusJSON(http.StatusBadRequest, types.Failed(ecode.BadArgument, "X-Amz-Date is expired"))
return return
} }


// 构造验签用的请求 // 构造验签用的请求
verifyReq, err := http.NewRequest(c.Request.Method, c.Request.URL.String(), nil) verifyReq, err := http.NewRequest(c.Request.Method, c.Request.URL.String(), nil)
if err != nil { if err != nil {
c.AbortWithStatusJSON(http.StatusOK, Failed(errorcode.OperationFailed, err.Error()))
c.AbortWithStatusJSON(http.StatusOK, types.Failed(ecode.OperationFailed, err.Error()))
return return
} }
for _, h := range headers { for _, h := range headers {
@@ -173,14 +174,14 @@ func (a *AWSAuth) AuthWithoutBody(c *gin.Context) {


if err != nil { if err != nil {
logger.Warnf("sign request: %v", err) logger.Warnf("sign request: %v", err)
c.AbortWithStatusJSON(http.StatusOK, Failed(errorcode.OperationFailed, "sign request failed"))
c.AbortWithStatusJSON(http.StatusOK, types.Failed(ecode.OperationFailed, "sign request failed"))
return return
} }


verifySig := getSignatureFromAWSHeader(verifyReq) verifySig := getSignatureFromAWSHeader(verifyReq)
if !strings.EqualFold(verifySig, reqSig) { if !strings.EqualFold(verifySig, reqSig) {
logger.Warnf("signature mismatch, input header: %s, verify: %s", authorizationHeader, verifySig) logger.Warnf("signature mismatch, input header: %s, verify: %s", authorizationHeader, verifySig)
c.AbortWithStatusJSON(http.StatusOK, Failed(errorcode.Unauthorized, "signature mismatch"))
c.AbortWithStatusJSON(http.StatusOK, types.Failed(ecode.Unauthorized, "signature mismatch"))
return return
} }


@@ -198,7 +199,7 @@ func (a *AWSAuth) PresignedAuth(c *gin.Context) {
signature := query.Get("X-Amz-Signature") signature := query.Get("X-Amz-Signature")
query.Del("X-Amz-Signature") query.Del("X-Amz-Signature")
if signature == "" { if signature == "" {
c.AbortWithStatusJSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing X-Amz-Signature query parameter"))
c.AbortWithStatusJSON(http.StatusBadRequest, types.Failed(ecode.BadArgument, "missing X-Amz-Signature query parameter"))
return return
} }


@@ -209,7 +210,7 @@ func (a *AWSAuth) PresignedAuth(c *gin.Context) {
expiresStr := query.Get("X-Expires") expiresStr := query.Get("X-Expires")
expires, err := strconv.ParseInt(expiresStr, 10, 64) expires, err := strconv.ParseInt(expiresStr, 10, 64)
if err != nil { if err != nil {
c.AbortWithStatusJSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "invalid X-Expires format"))
c.AbortWithStatusJSON(http.StatusBadRequest, types.Failed(ecode.BadArgument, "invalid X-Expires format"))
return return
} }


@@ -219,7 +220,7 @@ func (a *AWSAuth) PresignedAuth(c *gin.Context) {


verifyReq, err := http.NewRequest(c.Request.Method, c.Request.URL.String(), nil) verifyReq, err := http.NewRequest(c.Request.Method, c.Request.URL.String(), nil)
if err != nil { if err != nil {
c.AbortWithStatusJSON(http.StatusOK, Failed(errorcode.OperationFailed, err.Error()))
c.AbortWithStatusJSON(http.StatusOK, types.Failed(ecode.OperationFailed, err.Error()))
return return
} }
for _, h := range signedHeaders { for _, h := range signedHeaders {
@@ -234,26 +235,26 @@ func (a *AWSAuth) PresignedAuth(c *gin.Context) {


timestamp, err := time.Parse("20060102T150405Z", date) timestamp, err := time.Parse("20060102T150405Z", date)
if err != nil { if err != nil {
c.AbortWithStatusJSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "invalid X-Amz-Date format"))
c.AbortWithStatusJSON(http.StatusBadRequest, types.Failed(ecode.BadArgument, "invalid X-Amz-Date format"))
return return
} }


if time.Now().After(timestamp.Add(time.Duration(expires) * time.Second)) { if time.Now().After(timestamp.Add(time.Duration(expires) * time.Second)) {
c.AbortWithStatusJSON(http.StatusUnauthorized, Failed(errorcode.Unauthorized, "request expired"))
c.AbortWithStatusJSON(http.StatusUnauthorized, types.Failed(ecode.Unauthorized, "request expired"))
return return
} }


signer := v4.NewSigner() signer := v4.NewSigner()
uri, _, err := signer.PresignHTTP(context.TODO(), a.cred, verifyReq, "", AuthService, AuthRegion, timestamp) uri, _, err := signer.PresignHTTP(context.TODO(), a.cred, verifyReq, "", AuthService, AuthRegion, timestamp)
if err != nil { if err != nil {
c.AbortWithStatusJSON(http.StatusOK, Failed(errorcode.OperationFailed, "sign request failed"))
c.AbortWithStatusJSON(http.StatusOK, types.Failed(ecode.OperationFailed, "sign request failed"))
return return
} }


verifySig := getSignatureFromAWSQuery(uri) verifySig := getSignatureFromAWSQuery(uri)
if !strings.EqualFold(verifySig, signature) { if !strings.EqualFold(verifySig, signature) {
logger.Warnf("signature mismatch, input: %s, verify: %s", signature, verifySig) logger.Warnf("signature mismatch, input: %s, verify: %s", signature, verifySig)
c.AbortWithStatusJSON(http.StatusOK, Failed(errorcode.Unauthorized, "signature mismatch"))
c.AbortWithStatusJSON(http.StatusOK, types.Failed(ecode.Unauthorized, "signature mismatch"))
return return
} }


@@ -319,3 +320,4 @@ func getSignatureFromAWSQuery(uri string) string {


return uri[idx+len("X-Amz-Signature=") : andIdx] return uri[idx+len("X-Amz-Signature=") : andIdx]
} }
*/

+ 14
- 13
client/internal/http/v1/bucket.go View File

@@ -6,9 +6,10 @@ import (
"time" "time"


"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"gitlink.org.cn/cloudream/common/consts/errorcode"
"gitlink.org.cn/cloudream/common/pkgs/logger" "gitlink.org.cn/cloudream/common/pkgs/logger"
"gitlink.org.cn/cloudream/jcs-pub/client/internal/http/types"
cliapi "gitlink.org.cn/cloudream/jcs-pub/client/sdk/api/v1" cliapi "gitlink.org.cn/cloudream/jcs-pub/client/sdk/api/v1"
"gitlink.org.cn/cloudream/jcs-pub/common/ecode"
) )


type BucketService struct { type BucketService struct {
@@ -27,18 +28,18 @@ func (s *BucketService) GetByName(ctx *gin.Context) {
var req cliapi.BucketGetByName var req cliapi.BucketGetByName
if err := ctx.ShouldBindQuery(&req); err != nil { if err := ctx.ShouldBindQuery(&req); err != nil {
log.Warnf("binding query: %s", err.Error()) log.Warnf("binding query: %s", err.Error())
ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
ctx.JSON(http.StatusBadRequest, types.Failed(ecode.BadArgument, "missing argument or invalid argument"))
return return
} }


bucket, err := s.svc.BucketSvc().GetBucketByName(req.Name) bucket, err := s.svc.BucketSvc().GetBucketByName(req.Name)
if err != nil { if err != nil {
log.Warnf("getting bucket by name: %s", err.Error()) log.Warnf("getting bucket by name: %s", err.Error())
ctx.JSON(http.StatusOK, FailedError(err))
ctx.JSON(http.StatusOK, types.FailedError(err))
return return
} }


ctx.JSON(http.StatusOK, OK(cliapi.BucketGetByNameResp{
ctx.JSON(http.StatusOK, types.OK(cliapi.BucketGetByNameResp{
Bucket: bucket, Bucket: bucket,
})) }))
} }
@@ -49,18 +50,18 @@ func (s *BucketService) Create(ctx *gin.Context) {
var req cliapi.BucketCreate var req cliapi.BucketCreate
if err := ctx.ShouldBindJSON(&req); err != nil { if err := ctx.ShouldBindJSON(&req); err != nil {
log.Warnf("binding body: %s", err.Error()) log.Warnf("binding body: %s", err.Error())
ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
ctx.JSON(http.StatusBadRequest, types.Failed(ecode.BadArgument, "missing argument or invalid argument"))
return return
} }


bucket, err := s.svc.BucketSvc().CreateBucket(req.Name, time.Now()) bucket, err := s.svc.BucketSvc().CreateBucket(req.Name, time.Now())
if err != nil { if err != nil {
log.Warnf("creating bucket: %s", err.Error()) log.Warnf("creating bucket: %s", err.Error())
ctx.JSON(http.StatusOK, FailedError(err))
ctx.JSON(http.StatusOK, types.FailedError(err))
return return
} }


ctx.JSON(http.StatusOK, OK(cliapi.BucketCreateResp{
ctx.JSON(http.StatusOK, types.OK(cliapi.BucketCreateResp{
Bucket: bucket, Bucket: bucket,
})) }))
} }
@@ -71,17 +72,17 @@ func (s *BucketService) Delete(ctx *gin.Context) {
var req cliapi.BucketDelete var req cliapi.BucketDelete
if err := ctx.ShouldBindJSON(&req); err != nil { if err := ctx.ShouldBindJSON(&req); err != nil {
log.Warnf("binding body: %s", err.Error()) log.Warnf("binding body: %s", err.Error())
ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
ctx.JSON(http.StatusBadRequest, types.Failed(ecode.BadArgument, "missing argument or invalid argument"))
return return
} }


if err := s.svc.BucketSvc().DeleteBucket(req.BucketID); err != nil { if err := s.svc.BucketSvc().DeleteBucket(req.BucketID); err != nil {
log.Warnf("deleting bucket: %s", err.Error()) log.Warnf("deleting bucket: %s", err.Error())
ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "delete bucket failed"))
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, "delete bucket types.Failed"))
return return
} }


ctx.JSON(http.StatusOK, OK(nil))
ctx.JSON(http.StatusOK, types.OK(nil))
} }


func (s *BucketService) ListAll(ctx *gin.Context) { func (s *BucketService) ListAll(ctx *gin.Context) {
@@ -90,18 +91,18 @@ func (s *BucketService) ListAll(ctx *gin.Context) {
var req cliapi.BucketListAll var req cliapi.BucketListAll
if err := ctx.ShouldBindQuery(&req); err != nil { if err := ctx.ShouldBindQuery(&req); err != nil {
log.Warnf("binding query: %s", err.Error()) log.Warnf("binding query: %s", err.Error())
ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
ctx.JSON(http.StatusBadRequest, types.Failed(ecode.BadArgument, "missing argument or invalid argument"))
return return
} }


buckets, err := s.svc.BucketSvc().ListAllBuckets() buckets, err := s.svc.BucketSvc().ListAllBuckets()
if err != nil { if err != nil {
log.Warnf("list all buckets: %s", err.Error()) log.Warnf("list all buckets: %s", err.Error())
ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, fmt.Sprintf("list all buckets: %v", err)))
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, fmt.Sprintf("list all buckets: %v", err)))
return return
} }


ctx.JSON(http.StatusOK, OK(cliapi.BucketListAllResp{
ctx.JSON(http.StatusOK, types.OK(cliapi.BucketListAllResp{
Buckets: buckets, Buckets: buckets,
})) }))
} }

+ 6
- 5
client/internal/http/v1/mount.go View File

@@ -4,9 +4,10 @@ import (
"net/http" "net/http"


"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"gitlink.org.cn/cloudream/common/consts/errorcode"
"gitlink.org.cn/cloudream/common/pkgs/logger" "gitlink.org.cn/cloudream/common/pkgs/logger"
"gitlink.org.cn/cloudream/jcs-pub/client/internal/http/types"
cliapi "gitlink.org.cn/cloudream/jcs-pub/client/sdk/api/v1" cliapi "gitlink.org.cn/cloudream/jcs-pub/client/sdk/api/v1"
"gitlink.org.cn/cloudream/jcs-pub/common/ecode"
) )


type MountService struct { type MountService struct {
@@ -25,12 +26,12 @@ func (m *MountService) DumpStatus(ctx *gin.Context) {
var req cliapi.MountDumpStatus var req cliapi.MountDumpStatus
if err := ctx.ShouldBindQuery(&req); err != nil { if err := ctx.ShouldBindQuery(&req); err != nil {
log.Warnf("binding body: %s", err.Error()) log.Warnf("binding body: %s", err.Error())
ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
ctx.JSON(http.StatusBadRequest, types.Failed(ecode.BadArgument, "missing argument or invalid argument"))
return return
} }


dumpStatus := m.svc.Mount.Dump() dumpStatus := m.svc.Mount.Dump()
ctx.JSON(http.StatusOK, OK(cliapi.MountDumpStatusResp{
ctx.JSON(http.StatusOK, types.OK(cliapi.MountDumpStatusResp{
MountStatus: dumpStatus, MountStatus: dumpStatus,
})) }))
} }
@@ -40,10 +41,10 @@ func (m *MountService) StartReclaimSpace(ctx *gin.Context) {
// var req cliapi.MountReclaimSpace // var req cliapi.MountReclaimSpace
// if err := ctx.ShouldBindJSON(&req); err != nil { // if err := ctx.ShouldBindJSON(&req); err != nil {
// log.Warnf("binding body: %s", err.Error()) // log.Warnf("binding body: %s", err.Error())
// ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
// ctx.JSON(http.StatusBadRequest, types.Failed(ecode.BadArgument, "missing argument or invalid argument"))
// return // return
// } // }


m.svc.Mount.StartReclaimSpace() m.svc.Mount.StartReclaimSpace()
ctx.JSON(http.StatusOK, OK(cliapi.StartMountReclaimSpaceResp{}))
ctx.JSON(http.StatusOK, types.OK(cliapi.StartMountReclaimSpaceResp{}))
} }

+ 57
- 56
client/internal/http/v1/object.go View File

@@ -10,12 +10,13 @@ import (
"path/filepath" "path/filepath"


"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"gitlink.org.cn/cloudream/common/consts/errorcode"
"gitlink.org.cn/cloudream/common/pkgs/logger" "gitlink.org.cn/cloudream/common/pkgs/logger"
"gitlink.org.cn/cloudream/common/utils/math2" "gitlink.org.cn/cloudream/common/utils/math2"
"gitlink.org.cn/cloudream/jcs-pub/client/internal/downloader" "gitlink.org.cn/cloudream/jcs-pub/client/internal/downloader"
"gitlink.org.cn/cloudream/jcs-pub/client/internal/http/types"
cliapi "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" clitypes "gitlink.org.cn/cloudream/jcs-pub/client/types"
"gitlink.org.cn/cloudream/jcs-pub/common/ecode"
) )


type ObjectService struct { type ObjectService struct {
@@ -34,18 +35,18 @@ func (s *ObjectService) ListByPath(ctx *gin.Context) {
var req cliapi.ObjectListByPath var req cliapi.ObjectListByPath
if err := ctx.ShouldBindQuery(&req); err != nil { if err := ctx.ShouldBindQuery(&req); err != nil {
log.Warnf("binding body: %s", err.Error()) log.Warnf("binding body: %s", err.Error())
ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
ctx.JSON(http.StatusBadRequest, types.Failed(ecode.BadArgument, "missing argument or invalid argument"))
return return
} }


resp, err := s.svc.ObjectSvc().GetByPath(req) resp, err := s.svc.ObjectSvc().GetByPath(req)
if err != nil { if err != nil {
log.Warnf("listing objects: %s", err.Error()) log.Warnf("listing objects: %s", err.Error())
ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, fmt.Sprintf("listing objects: %v", err)))
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, fmt.Sprintf("listing objects: %v", err)))
return return
} }


ctx.JSON(http.StatusOK, OK(resp))
ctx.JSON(http.StatusOK, types.OK(resp))
} }


func (s *ObjectService) ListByIDs(ctx *gin.Context) { func (s *ObjectService) ListByIDs(ctx *gin.Context) {
@@ -54,18 +55,18 @@ func (s *ObjectService) ListByIDs(ctx *gin.Context) {
var req cliapi.ObjectListByIDs var req cliapi.ObjectListByIDs
if err := ctx.ShouldBindQuery(&req); err != nil { if err := ctx.ShouldBindQuery(&req); err != nil {
log.Warnf("binding body: %s", err.Error()) log.Warnf("binding body: %s", err.Error())
ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
ctx.JSON(http.StatusBadRequest, types.Failed(ecode.BadArgument, "missing argument or invalid argument"))
return return
} }


objs, err := s.svc.ObjectSvc().GetByIDs(req.ObjectIDs) objs, err := s.svc.ObjectSvc().GetByIDs(req.ObjectIDs)
if err != nil { if err != nil {
log.Warnf("listing objects: %s", err.Error()) log.Warnf("listing objects: %s", err.Error())
ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, fmt.Sprintf("listing objects: %v", err)))
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, fmt.Sprintf("listing objects: %v", err)))
return return
} }


ctx.JSON(http.StatusOK, OK(cliapi.ObjectListByIDsResp{Objects: objs}))
ctx.JSON(http.StatusOK, types.OK(cliapi.ObjectListByIDsResp{Objects: objs}))
} }


type ObjectUploadReq struct { type ObjectUploadReq struct {
@@ -79,14 +80,14 @@ func (s *ObjectService) Upload(ctx *gin.Context) {
var req ObjectUploadReq var req ObjectUploadReq
if err := ctx.ShouldBind(&req); err != nil { if err := ctx.ShouldBind(&req); err != nil {
log.Warnf("binding body: %s", err.Error()) log.Warnf("binding body: %s", err.Error())
ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
ctx.JSON(http.StatusBadRequest, types.Failed(ecode.BadArgument, "missing argument or invalid argument"))
return return
} }


up, err := s.svc.Uploader.BeginUpdate(req.Info.PackageID, req.Info.Affinity, req.Info.CopyTo, req.Info.CopyToPath) up, err := s.svc.Uploader.BeginUpdate(req.Info.PackageID, req.Info.Affinity, req.Info.CopyTo, req.Info.CopyToPath)
if err != nil { if err != nil {
log.Warnf("begin update: %s", err.Error()) log.Warnf("begin update: %s", err.Error())
ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, fmt.Sprintf("begin update: %v", err)))
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, fmt.Sprintf("begin update: %v", err)))
return return
} }
defer up.Abort() defer up.Abort()
@@ -96,14 +97,14 @@ func (s *ObjectService) Upload(ctx *gin.Context) {
f, err := file.Open() f, err := file.Open()
if err != nil { if err != nil {
log.Warnf("open file: %s", err.Error()) log.Warnf("open file: %s", err.Error())
ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, fmt.Sprintf("open file %v: %v", file.Filename, err)))
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, fmt.Sprintf("open file %v: %v", file.Filename, err)))
return return
} }


path, err := url.PathUnescape(file.Filename) path, err := url.PathUnescape(file.Filename)
if err != nil { if err != nil {
log.Warnf("unescape filename: %s", err.Error()) log.Warnf("unescape filename: %s", err.Error())
ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, fmt.Sprintf("unescape filename %v: %v", file.Filename, err)))
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, fmt.Sprintf("unescape filename %v: %v", file.Filename, err)))
return return
} }
path = filepath.ToSlash(path) path = filepath.ToSlash(path)
@@ -111,7 +112,7 @@ func (s *ObjectService) Upload(ctx *gin.Context) {
err = up.Upload(path, f) err = up.Upload(path, f)
if err != nil { if err != nil {
log.Warnf("uploading file: %s", err.Error()) log.Warnf("uploading file: %s", err.Error())
ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, fmt.Sprintf("uploading file %v: %v", file.Filename, err)))
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, fmt.Sprintf("uploading file %v: %v", file.Filename, err)))
return return
} }
pathes = append(pathes, path) pathes = append(pathes, path)
@@ -120,7 +121,7 @@ func (s *ObjectService) Upload(ctx *gin.Context) {
ret, err := up.Commit() ret, err := up.Commit()
if err != nil { if err != nil {
log.Warnf("commit update: %s", err.Error()) log.Warnf("commit update: %s", err.Error())
ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, fmt.Sprintf("commit update: %v", err)))
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, fmt.Sprintf("commit update: %v", err)))
return return
} }


@@ -129,7 +130,7 @@ func (s *ObjectService) Upload(ctx *gin.Context) {
uploadeds[i] = ret.Objects[pathes[i]] uploadeds[i] = ret.Objects[pathes[i]]
} }


ctx.JSON(http.StatusOK, OK(cliapi.ObjectUploadResp{Uploadeds: uploadeds}))
ctx.JSON(http.StatusOK, types.OK(cliapi.ObjectUploadResp{Uploadeds: uploadeds}))
} }


func (s *ObjectService) Download(ctx *gin.Context) { func (s *ObjectService) Download(ctx *gin.Context) {
@@ -138,7 +139,7 @@ func (s *ObjectService) Download(ctx *gin.Context) {
var req cliapi.ObjectDownload var req cliapi.ObjectDownload
if err := ctx.ShouldBindQuery(&req); err != nil { if err := ctx.ShouldBindQuery(&req); err != nil {
log.Warnf("binding body: %s", err.Error()) log.Warnf("binding body: %s", err.Error())
ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
ctx.JSON(http.StatusBadRequest, types.Failed(ecode.BadArgument, "missing argument or invalid argument"))
return return
} }


@@ -155,12 +156,12 @@ func (s *ObjectService) Download(ctx *gin.Context) {
}) })
if err != nil { if err != nil {
log.Warnf("downloading object: %s", err.Error()) log.Warnf("downloading object: %s", err.Error())
ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "download object failed"))
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, "download object failed"))
return return
} }
if file.File == nil { if file.File == nil {
log.Warnf("object not found: %d", req.ObjectID) log.Warnf("object not found: %d", req.ObjectID)
ctx.JSON(http.StatusOK, Failed(errorcode.DataNotFound, "object not found"))
ctx.JSON(http.StatusOK, types.Failed(ecode.DataNotFound, "object not found"))
return return
} }
defer file.File.Close() defer file.File.Close()
@@ -186,7 +187,7 @@ func (s *ObjectService) DownloadByPath(ctx *gin.Context) {
var req cliapi.ObjectDownloadByPath var req cliapi.ObjectDownloadByPath
if err := ctx.ShouldBindQuery(&req); err != nil { if err := ctx.ShouldBindQuery(&req); err != nil {
log.Warnf("binding body: %s", err.Error()) log.Warnf("binding body: %s", err.Error())
ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
ctx.JSON(http.StatusBadRequest, types.Failed(ecode.BadArgument, "missing argument or invalid argument"))
return return
} }


@@ -195,13 +196,13 @@ func (s *ObjectService) DownloadByPath(ctx *gin.Context) {
}) })
if err != nil { if err != nil {
log.Warnf("getting object by path: %s", err.Error()) log.Warnf("getting object by path: %s", err.Error())
ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "get object by path failed"))
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, "get object by path failed"))
return return
} }


if len(resp.Objects) == 0 { if len(resp.Objects) == 0 {
log.Warnf("object not found: %s", req.Path) log.Warnf("object not found: %s", req.Path)
ctx.JSON(http.StatusOK, Failed(errorcode.DataNotFound, "object not found"))
ctx.JSON(http.StatusOK, types.Failed(ecode.DataNotFound, "object not found"))
return return
} }


@@ -218,7 +219,7 @@ func (s *ObjectService) DownloadByPath(ctx *gin.Context) {
}) })
if err != nil { if err != nil {
log.Warnf("downloading object: %s", err.Error()) log.Warnf("downloading object: %s", err.Error())
ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "download object failed"))
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, "download object failed"))
return return
} }
defer file.File.Close() defer file.File.Close()
@@ -243,18 +244,18 @@ func (s *ObjectService) UpdateInfo(ctx *gin.Context) {
var req cliapi.ObjectUpdateInfo var req cliapi.ObjectUpdateInfo
if err := ctx.ShouldBindJSON(&req); err != nil { if err := ctx.ShouldBindJSON(&req); err != nil {
log.Warnf("binding body: %s", err.Error()) log.Warnf("binding body: %s", err.Error())
ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
ctx.JSON(http.StatusBadRequest, types.Failed(ecode.BadArgument, "missing argument or invalid argument"))
return return
} }


sucs, err := s.svc.ObjectSvc().UpdateInfo(req.Updatings) sucs, err := s.svc.ObjectSvc().UpdateInfo(req.Updatings)
if err != nil { if err != nil {
log.Warnf("updating objects: %s", err.Error()) log.Warnf("updating objects: %s", err.Error())
ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "update objects failed"))
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, "update objects failed"))
return return
} }


ctx.JSON(http.StatusOK, OK(cliapi.ObjectUpdateInfoResp{Successes: sucs}))
ctx.JSON(http.StatusOK, types.OK(cliapi.ObjectUpdateInfoResp{Successes: sucs}))
} }


func (s *ObjectService) UpdateInfoByPath(ctx *gin.Context) { func (s *ObjectService) UpdateInfoByPath(ctx *gin.Context) {
@@ -263,7 +264,7 @@ func (s *ObjectService) UpdateInfoByPath(ctx *gin.Context) {
var req cliapi.ObjectUpdateInfoByPath var req cliapi.ObjectUpdateInfoByPath
if err := ctx.ShouldBindJSON(&req); err != nil { if err := ctx.ShouldBindJSON(&req); err != nil {
log.Warnf("binding body: %s", err.Error()) log.Warnf("binding body: %s", err.Error())
ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
ctx.JSON(http.StatusBadRequest, types.Failed(ecode.BadArgument, "missing argument or invalid argument"))
return return
} }


@@ -272,12 +273,12 @@ func (s *ObjectService) UpdateInfoByPath(ctx *gin.Context) {
}) })
if err != nil { if err != nil {
log.Warnf("getting object by path: %s", err.Error()) log.Warnf("getting object by path: %s", err.Error())
ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "get object by path failed"))
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, "get object by path failed"))
return return
} }
if len(resp.Objects) == 0 { if len(resp.Objects) == 0 {
log.Warnf("object not found: %s", req.Path) log.Warnf("object not found: %s", req.Path)
ctx.JSON(http.StatusOK, Failed(errorcode.DataNotFound, "object not found"))
ctx.JSON(http.StatusOK, types.Failed(ecode.DataNotFound, "object not found"))
return return
} }


@@ -287,13 +288,13 @@ func (s *ObjectService) UpdateInfoByPath(ctx *gin.Context) {
}}) }})
if err != nil { if err != nil {
log.Warnf("updating objects: %s", err.Error()) log.Warnf("updating objects: %s", err.Error())
ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "update objects failed"))
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, "update objects failed"))
return return
} }
if len(sucs) == 0 { if len(sucs) == 0 {
} }


ctx.JSON(http.StatusOK, OK(cliapi.ObjectUpdateInfoByPathResp{}))
ctx.JSON(http.StatusOK, types.OK(cliapi.ObjectUpdateInfoByPathResp{}))
} }


func (s *ObjectService) Move(ctx *gin.Context) { func (s *ObjectService) Move(ctx *gin.Context) {
@@ -302,18 +303,18 @@ func (s *ObjectService) Move(ctx *gin.Context) {
var req cliapi.ObjectMove var req cliapi.ObjectMove
if err := ctx.ShouldBindJSON(&req); err != nil { if err := ctx.ShouldBindJSON(&req); err != nil {
log.Warnf("binding body: %s", err.Error()) log.Warnf("binding body: %s", err.Error())
ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
ctx.JSON(http.StatusBadRequest, types.Failed(ecode.BadArgument, "missing argument or invalid argument"))
return return
} }


sucs, err := s.svc.ObjectSvc().Move(req.Movings) sucs, err := s.svc.ObjectSvc().Move(req.Movings)
if err != nil { if err != nil {
log.Warnf("moving objects: %s", err.Error()) log.Warnf("moving objects: %s", err.Error())
ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "move objects failed"))
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, "move objects failed"))
return return
} }


ctx.JSON(http.StatusOK, OK(cliapi.ObjectMoveResp{Successes: sucs}))
ctx.JSON(http.StatusOK, types.OK(cliapi.ObjectMoveResp{Successes: sucs}))
} }


func (s *ObjectService) Delete(ctx *gin.Context) { func (s *ObjectService) Delete(ctx *gin.Context) {
@@ -322,18 +323,18 @@ func (s *ObjectService) Delete(ctx *gin.Context) {
var req cliapi.ObjectDelete var req cliapi.ObjectDelete
if err := ctx.ShouldBindJSON(&req); err != nil { if err := ctx.ShouldBindJSON(&req); err != nil {
log.Warnf("binding body: %s", err.Error()) log.Warnf("binding body: %s", err.Error())
ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
ctx.JSON(http.StatusBadRequest, types.Failed(ecode.BadArgument, "missing argument or invalid argument"))
return return
} }


err := s.svc.ObjectSvc().Delete(req.ObjectIDs) err := s.svc.ObjectSvc().Delete(req.ObjectIDs)
if err != nil { if err != nil {
log.Warnf("deleting objects: %s", err.Error()) log.Warnf("deleting objects: %s", err.Error())
ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "delete objects failed"))
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, "delete objects failed"))
return return
} }


ctx.JSON(http.StatusOK, OK(nil))
ctx.JSON(http.StatusOK, types.OK(nil))
} }


func (s *ObjectService) DeleteByPath(ctx *gin.Context) { func (s *ObjectService) DeleteByPath(ctx *gin.Context) {
@@ -342,7 +343,7 @@ func (s *ObjectService) DeleteByPath(ctx *gin.Context) {
var req cliapi.ObjectDeleteByPath var req cliapi.ObjectDeleteByPath
if err := ctx.ShouldBindJSON(&req); err != nil { if err := ctx.ShouldBindJSON(&req); err != nil {
log.Warnf("binding body: %s", err.Error()) log.Warnf("binding body: %s", err.Error())
ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
ctx.JSON(http.StatusBadRequest, types.Failed(ecode.BadArgument, "missing argument or invalid argument"))
return return
} }


@@ -351,22 +352,22 @@ func (s *ObjectService) DeleteByPath(ctx *gin.Context) {
}) })
if err != nil { if err != nil {
log.Warnf("getting object by path: %s", err.Error()) log.Warnf("getting object by path: %s", err.Error())
ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "get object by path failed"))
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, "get object by path failed"))
return return
} }
if len(resp.Objects) == 0 { if len(resp.Objects) == 0 {
ctx.JSON(http.StatusOK, OK(nil))
ctx.JSON(http.StatusOK, types.OK(nil))
return return
} }


err = s.svc.ObjectSvc().Delete([]clitypes.ObjectID{resp.Objects[0].ObjectID}) err = s.svc.ObjectSvc().Delete([]clitypes.ObjectID{resp.Objects[0].ObjectID})
if err != nil { if err != nil {
log.Warnf("deleting objects: %s", err.Error()) log.Warnf("deleting objects: %s", err.Error())
ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "delete objects failed"))
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, "delete objects failed"))
return return
} }


ctx.JSON(http.StatusOK, OK(nil))
ctx.JSON(http.StatusOK, types.OK(nil))
} }


func (s *ObjectService) Clone(ctx *gin.Context) { func (s *ObjectService) Clone(ctx *gin.Context) {
@@ -375,18 +376,18 @@ func (s *ObjectService) Clone(ctx *gin.Context) {
var req cliapi.ObjectClone var req cliapi.ObjectClone
if err := ctx.ShouldBindJSON(&req); err != nil { if err := ctx.ShouldBindJSON(&req); err != nil {
log.Warnf("binding body: %s", err.Error()) log.Warnf("binding body: %s", err.Error())
ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
ctx.JSON(http.StatusBadRequest, types.Failed(ecode.BadArgument, "missing argument or invalid argument"))
return return
} }


objs, err := s.svc.ObjectSvc().Clone(req.Clonings) objs, err := s.svc.ObjectSvc().Clone(req.Clonings)
if err != nil { if err != nil {
log.Warnf("cloning object: %s", err.Error()) log.Warnf("cloning object: %s", err.Error())
ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "clone object failed"))
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, "clone object failed"))
return return
} }


ctx.JSON(http.StatusOK, OK(cliapi.ObjectCloneResp{Objects: objs}))
ctx.JSON(http.StatusOK, types.OK(cliapi.ObjectCloneResp{Objects: objs}))
} }


func (s *ObjectService) GetPackageObjects(ctx *gin.Context) { func (s *ObjectService) GetPackageObjects(ctx *gin.Context) {
@@ -395,18 +396,18 @@ func (s *ObjectService) GetPackageObjects(ctx *gin.Context) {
var req cliapi.ObjectGetPackageObjects var req cliapi.ObjectGetPackageObjects
if err := ctx.ShouldBindQuery(&req); err != nil { if err := ctx.ShouldBindQuery(&req); err != nil {
log.Warnf("binding body: %s", err.Error()) log.Warnf("binding body: %s", err.Error())
ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
ctx.JSON(http.StatusBadRequest, types.Failed(ecode.BadArgument, "missing argument or invalid argument"))
return return
} }


objs, err := s.svc.ObjectSvc().GetPackageObjects(req.PackageID) objs, err := s.svc.ObjectSvc().GetPackageObjects(req.PackageID)
if err != nil { if err != nil {
log.Warnf("getting package objects: %s", err.Error()) log.Warnf("getting package objects: %s", err.Error())
ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "get package object failed"))
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, "get package object failed"))
return return
} }


ctx.JSON(http.StatusOK, OK(cliapi.ObjectGetPackageObjectsResp{Objects: objs}))
ctx.JSON(http.StatusOK, types.OK(cliapi.ObjectGetPackageObjectsResp{Objects: objs}))
} }


func (s *ObjectService) NewMultipartUpload(ctx *gin.Context) { func (s *ObjectService) NewMultipartUpload(ctx *gin.Context) {
@@ -415,18 +416,18 @@ func (s *ObjectService) NewMultipartUpload(ctx *gin.Context) {
var req cliapi.ObjectNewMultipartUpload var req cliapi.ObjectNewMultipartUpload
if err := ctx.ShouldBindJSON(&req); err != nil { if err := ctx.ShouldBindJSON(&req); err != nil {
log.Warnf("binding body: %s", err.Error()) log.Warnf("binding body: %s", err.Error())
ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
ctx.JSON(http.StatusBadRequest, types.Failed(ecode.BadArgument, "missing argument or invalid argument"))
return return
} }


obj, err := s.svc.ObjectSvc().NewMultipartUploadObject(req.PackageID, req.Path) obj, err := s.svc.ObjectSvc().NewMultipartUploadObject(req.PackageID, req.Path)
if err != nil { if err != nil {
log.Warnf("new multipart upload object: %s", err.Error()) log.Warnf("new multipart upload object: %s", err.Error())
ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "new multipart upload object failed"))
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, "new multipart upload object failed"))
return return
} }


ctx.JSON(http.StatusOK, OK(cliapi.ObjectNewMultipartUploadResp{Object: obj}))
ctx.JSON(http.StatusOK, types.OK(cliapi.ObjectNewMultipartUploadResp{Object: obj}))
} }


type ObjectUploadPartReq struct { type ObjectUploadPartReq struct {
@@ -440,14 +441,14 @@ func (s *ObjectService) UploadPart(ctx *gin.Context) {
var req ObjectUploadPartReq var req ObjectUploadPartReq
if err := ctx.ShouldBind(&req); err != nil { if err := ctx.ShouldBind(&req); err != nil {
log.Warnf("binding body: %s", err.Error()) log.Warnf("binding body: %s", err.Error())
ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
ctx.JSON(http.StatusBadRequest, types.Failed(ecode.BadArgument, "missing argument or invalid argument"))
return return
} }


file, err := req.File.Open() file, err := req.File.Open()
if err != nil { if err != nil {
log.Warnf("open file: %s", err.Error()) log.Warnf("open file: %s", err.Error())
ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "open file failed"))
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, "open file failed"))
return return
} }
defer file.Close() defer file.Close()
@@ -455,11 +456,11 @@ func (s *ObjectService) UploadPart(ctx *gin.Context) {
err = s.svc.Uploader.UploadPart(req.Info.ObjectID, req.Info.Index, file) err = s.svc.Uploader.UploadPart(req.Info.ObjectID, req.Info.Index, file)
if err != nil { if err != nil {
log.Warnf("uploading part: %s", err.Error()) log.Warnf("uploading part: %s", err.Error())
ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, fmt.Sprintf("upload part: %v", err)))
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, fmt.Sprintf("upload part: %v", err)))
return return
} }


ctx.JSON(http.StatusOK, OK(cliapi.ObjectUploadPartResp{}))
ctx.JSON(http.StatusOK, types.OK(cliapi.ObjectUploadPartResp{}))
} }


func (s *ObjectService) CompleteMultipartUpload(ctx *gin.Context) { func (s *ObjectService) CompleteMultipartUpload(ctx *gin.Context) {
@@ -468,16 +469,16 @@ func (s *ObjectService) CompleteMultipartUpload(ctx *gin.Context) {
var req cliapi.ObjectCompleteMultipartUpload var req cliapi.ObjectCompleteMultipartUpload
if err := ctx.ShouldBindJSON(&req); err != nil { if err := ctx.ShouldBindJSON(&req); err != nil {
log.Warnf("binding body: %s", err.Error()) log.Warnf("binding body: %s", err.Error())
ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
ctx.JSON(http.StatusBadRequest, types.Failed(ecode.BadArgument, "missing argument or invalid argument"))
return return
} }


obj, err := s.svc.ObjectSvc().CompleteMultipartUpload(req.ObjectID, req.Indexes) obj, err := s.svc.ObjectSvc().CompleteMultipartUpload(req.ObjectID, req.Indexes)
if err != nil { if err != nil {
log.Warnf("completing multipart upload: %s", err.Error()) log.Warnf("completing multipart upload: %s", err.Error())
ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, fmt.Sprintf("complete multipart upload: %v", err)))
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, fmt.Sprintf("complete multipart upload: %v", err)))
return return
} }


ctx.JSON(http.StatusOK, OK(cliapi.ObjectCompleteMultipartUploadResp{Object: obj}))
ctx.JSON(http.StatusOK, types.OK(cliapi.ObjectCompleteMultipartUploadResp{Object: obj}))
} }

+ 28
- 27
client/internal/http/v1/package.go View File

@@ -8,10 +8,11 @@ import (
"path/filepath" "path/filepath"


"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"gitlink.org.cn/cloudream/common/consts/errorcode"
"gitlink.org.cn/cloudream/common/pkgs/logger" "gitlink.org.cn/cloudream/common/pkgs/logger"
"gitlink.org.cn/cloudream/jcs-pub/client/internal/http/types"
cliapi "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" clitypes "gitlink.org.cn/cloudream/jcs-pub/client/types"
"gitlink.org.cn/cloudream/jcs-pub/common/ecode"
) )


// PackageService 包服务,负责处理包相关的HTTP请求。 // PackageService 包服务,负责处理包相关的HTTP请求。
@@ -32,18 +33,18 @@ func (s *PackageService) Get(ctx *gin.Context) {
var req cliapi.PackageGetReq var req cliapi.PackageGetReq
if err := ctx.ShouldBindQuery(&req); err != nil { if err := ctx.ShouldBindQuery(&req); err != nil {
log.Warnf("binding body: %s", err.Error()) log.Warnf("binding body: %s", err.Error())
ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
ctx.JSON(http.StatusBadRequest, types.Failed(ecode.BadArgument, "missing argument or invalid argument"))
return return
} }


pkg, err := s.svc.PackageSvc().Get(req.PackageID) pkg, err := s.svc.PackageSvc().Get(req.PackageID)
if err != nil { if err != nil {
log.Warnf("getting package: %s", err.Error()) log.Warnf("getting package: %s", err.Error())
ctx.JSON(http.StatusOK, FailedError(err))
ctx.JSON(http.StatusOK, types.FailedError(err))
return return
} }


ctx.JSON(http.StatusOK, OK(cliapi.PackageGetResp{Package: pkg}))
ctx.JSON(http.StatusOK, types.OK(cliapi.PackageGetResp{Package: pkg}))
} }


func (s *PackageService) GetByFullName(ctx *gin.Context) { func (s *PackageService) GetByFullName(ctx *gin.Context) {
@@ -52,18 +53,18 @@ func (s *PackageService) GetByFullName(ctx *gin.Context) {
var req cliapi.PackageGetByFullName var req cliapi.PackageGetByFullName
if err := ctx.ShouldBindQuery(&req); err != nil { if err := ctx.ShouldBindQuery(&req); err != nil {
log.Warnf("binding query: %s", err.Error()) log.Warnf("binding query: %s", err.Error())
ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
ctx.JSON(http.StatusBadRequest, types.Failed(ecode.BadArgument, "missing argument or invalid argument"))
return return
} }


pkg, err := s.svc.PackageSvc().GetByFullName(req.BucketName, req.PackageName) pkg, err := s.svc.PackageSvc().GetByFullName(req.BucketName, req.PackageName)
if err != nil { if err != nil {
log.Warnf("getting package by name: %s", err.Error()) log.Warnf("getting package by name: %s", err.Error())
ctx.JSON(http.StatusOK, FailedError(err))
ctx.JSON(http.StatusOK, types.FailedError(err))
return return
} }


ctx.JSON(http.StatusOK, OK(cliapi.PackageGetByFullNameResp{Package: pkg}))
ctx.JSON(http.StatusOK, types.OK(cliapi.PackageGetByFullNameResp{Package: pkg}))
} }


// Create 处理创建新包的HTTP请求。 // Create 处理创建新包的HTTP请求。
@@ -72,18 +73,18 @@ func (s *PackageService) Create(ctx *gin.Context) {
var req cliapi.PackageCreate var req cliapi.PackageCreate
if err := ctx.ShouldBindJSON(&req); err != nil { if err := ctx.ShouldBindJSON(&req); err != nil {
log.Warnf("binding body: %s", err.Error()) log.Warnf("binding body: %s", err.Error())
ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
ctx.JSON(http.StatusBadRequest, types.Failed(ecode.BadArgument, "missing argument or invalid argument"))
return return
} }


pkg, err := s.svc.PackageSvc().Create(req.BucketID, req.Name) pkg, err := s.svc.PackageSvc().Create(req.BucketID, req.Name)
if err != nil { if err != nil {
log.Warnf("creating package: %s", err.Error()) log.Warnf("creating package: %s", err.Error())
ctx.JSON(http.StatusOK, FailedError(err))
ctx.JSON(http.StatusOK, types.FailedError(err))
return return
} }


ctx.JSON(http.StatusOK, OK(cliapi.PackageCreateResp{
ctx.JSON(http.StatusOK, types.OK(cliapi.PackageCreateResp{
Package: pkg, Package: pkg,
})) }))
} }
@@ -99,20 +100,20 @@ func (s *PackageService) CreateLoad(ctx *gin.Context) {
var req PackageCreateUpload var req PackageCreateUpload
if err := ctx.ShouldBind(&req); err != nil { if err := ctx.ShouldBind(&req); err != nil {
log.Warnf("binding body: %s", err.Error()) log.Warnf("binding body: %s", err.Error())
ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
ctx.JSON(http.StatusBadRequest, types.Failed(ecode.BadArgument, "missing argument or invalid argument"))
return return
} }


if len(req.Info.CopyTo) != len(req.Info.CopyToPath) { if len(req.Info.CopyTo) != len(req.Info.CopyToPath) {
log.Warnf("CopyTo and CopyToPath count not match") log.Warnf("CopyTo and CopyToPath count not match")
ctx.JSON(http.StatusOK, Failed(errorcode.BadArgument, "CopyTo and CopyToPath count not match"))
ctx.JSON(http.StatusOK, types.Failed(ecode.BadArgument, "CopyTo and CopyToPath count not match"))
return return
} }


up, err := s.svc.Uploader.BeginCreateUpload(req.Info.BucketID, req.Info.Name, req.Info.CopyTo, req.Info.CopyToPath) up, err := s.svc.Uploader.BeginCreateUpload(req.Info.BucketID, req.Info.Name, req.Info.CopyTo, req.Info.CopyToPath)
if err != nil { if err != nil {
log.Warnf("begin package create upload: %s", err.Error()) log.Warnf("begin package create upload: %s", err.Error())
ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "%v", err))
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, "%v", err))
return return
} }
defer up.Abort() defer up.Abort()
@@ -122,14 +123,14 @@ func (s *PackageService) CreateLoad(ctx *gin.Context) {
f, err := file.Open() f, err := file.Open()
if err != nil { if err != nil {
log.Warnf("open file: %s", err.Error()) log.Warnf("open file: %s", err.Error())
ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, fmt.Sprintf("open file %v: %v", file.Filename, err)))
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, fmt.Sprintf("open file %v: %v", file.Filename, err)))
return return
} }


path, err := url.PathUnescape(file.Filename) path, err := url.PathUnescape(file.Filename)
if err != nil { if err != nil {
log.Warnf("unescape filename: %s", err.Error()) log.Warnf("unescape filename: %s", err.Error())
ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, fmt.Sprintf("unescape filename %v: %v", file.Filename, err)))
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, fmt.Sprintf("unescape filename %v: %v", file.Filename, err)))
return return
} }
path = filepath.ToSlash(path) path = filepath.ToSlash(path)
@@ -137,7 +138,7 @@ func (s *PackageService) CreateLoad(ctx *gin.Context) {
err = up.Upload(path, f) err = up.Upload(path, f)
if err != nil { if err != nil {
log.Warnf("uploading file: %s", err.Error()) log.Warnf("uploading file: %s", err.Error())
ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, fmt.Sprintf("uploading file %v: %v", file.Filename, err)))
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, fmt.Sprintf("uploading file %v: %v", file.Filename, err)))
return return
} }
pathes = append(pathes, path) pathes = append(pathes, path)
@@ -146,7 +147,7 @@ func (s *PackageService) CreateLoad(ctx *gin.Context) {
ret, err := up.Commit() ret, err := up.Commit()
if err != nil { if err != nil {
log.Warnf("commit create upload: %s", err.Error()) log.Warnf("commit create upload: %s", err.Error())
ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, fmt.Sprintf("commit create upload: %v", err)))
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, fmt.Sprintf("commit create upload: %v", err)))
return return
} }


@@ -155,7 +156,7 @@ func (s *PackageService) CreateLoad(ctx *gin.Context) {
objs[i] = ret.Objects[pathes[i]] objs[i] = ret.Objects[pathes[i]]
} }


ctx.JSON(http.StatusOK, OK(cliapi.PackageCreateUploadResp{Package: ret.Package, Objects: objs}))
ctx.JSON(http.StatusOK, types.OK(cliapi.PackageCreateUploadResp{Package: ret.Package, Objects: objs}))


} }
func (s *PackageService) Delete(ctx *gin.Context) { func (s *PackageService) Delete(ctx *gin.Context) {
@@ -164,18 +165,18 @@ func (s *PackageService) Delete(ctx *gin.Context) {
var req cliapi.PackageDelete var req cliapi.PackageDelete
if err := ctx.ShouldBindJSON(&req); err != nil { if err := ctx.ShouldBindJSON(&req); err != nil {
log.Warnf("binding body: %s", err.Error()) log.Warnf("binding body: %s", err.Error())
ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
ctx.JSON(http.StatusBadRequest, types.Failed(ecode.BadArgument, "missing argument or invalid argument"))
return return
} }


err := s.svc.PackageSvc().DeletePackage(req.PackageID) err := s.svc.PackageSvc().DeletePackage(req.PackageID)
if err != nil { if err != nil {
log.Warnf("deleting package: %s", err.Error()) log.Warnf("deleting package: %s", err.Error())
ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "delete package failed"))
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, "delete package failed"))
return return
} }


ctx.JSON(http.StatusOK, OK(nil))
ctx.JSON(http.StatusOK, types.OK(nil))
} }


func (s *PackageService) Clone(ctx *gin.Context) { func (s *PackageService) Clone(ctx *gin.Context) {
@@ -184,18 +185,18 @@ func (s *PackageService) Clone(ctx *gin.Context) {
var req cliapi.PackageClone var req cliapi.PackageClone
if err := ctx.ShouldBindJSON(&req); err != nil { if err := ctx.ShouldBindJSON(&req); err != nil {
log.Warnf("binding body: %s", err.Error()) log.Warnf("binding body: %s", err.Error())
ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
ctx.JSON(http.StatusBadRequest, types.Failed(ecode.BadArgument, "missing argument or invalid argument"))
return return
} }


pkg, err := s.svc.PackageSvc().Clone(req.PackageID, req.BucketID, req.Name) pkg, err := s.svc.PackageSvc().Clone(req.PackageID, req.BucketID, req.Name)
if err != nil { if err != nil {
log.Warnf("cloning package: %s", err.Error()) log.Warnf("cloning package: %s", err.Error())
ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "clone package failed"))
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, "clone package failed"))
return return
} }


ctx.JSON(http.StatusOK, OK(cliapi.PackageCloneResp{
ctx.JSON(http.StatusOK, types.OK(cliapi.PackageCloneResp{
Package: pkg, Package: pkg,
})) }))
} }
@@ -206,18 +207,18 @@ func (s *PackageService) ListBucketPackages(ctx *gin.Context) {
var req cliapi.PackageListBucketPackages var req cliapi.PackageListBucketPackages
if err := ctx.ShouldBindQuery(&req); err != nil { if err := ctx.ShouldBindQuery(&req); err != nil {
log.Warnf("binding query: %s", err.Error()) log.Warnf("binding query: %s", err.Error())
ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
ctx.JSON(http.StatusBadRequest, types.Failed(ecode.BadArgument, "missing argument or invalid argument"))
return return
} }


pkgs, err := s.svc.PackageSvc().GetBucketPackages(req.BucketID) pkgs, err := s.svc.PackageSvc().GetBucketPackages(req.BucketID)
if err != nil { if err != nil {
log.Warnf("getting bucket packages: %s", err.Error()) log.Warnf("getting bucket packages: %s", err.Error())
ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "get bucket packages failed"))
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, "get bucket packages failed"))
return return
} }


ctx.JSON(http.StatusOK, OK(cliapi.PackageListBucketPackagesResp{
ctx.JSON(http.StatusOK, types.OK(cliapi.PackageListBucketPackagesResp{
Packages: pkgs, Packages: pkgs,
})) }))
} }

+ 25
- 24
client/internal/http/v1/presigned.go View File

@@ -9,11 +9,12 @@ import (
"path/filepath" "path/filepath"


"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"gitlink.org.cn/cloudream/common/consts/errorcode"
"gitlink.org.cn/cloudream/common/pkgs/logger" "gitlink.org.cn/cloudream/common/pkgs/logger"
"gitlink.org.cn/cloudream/common/utils/math2" "gitlink.org.cn/cloudream/common/utils/math2"
"gitlink.org.cn/cloudream/jcs-pub/client/internal/downloader" "gitlink.org.cn/cloudream/jcs-pub/client/internal/downloader"
"gitlink.org.cn/cloudream/jcs-pub/client/internal/http/types"
cliapi "gitlink.org.cn/cloudream/jcs-pub/client/sdk/api/v1" cliapi "gitlink.org.cn/cloudream/jcs-pub/client/sdk/api/v1"
"gitlink.org.cn/cloudream/jcs-pub/common/ecode"
) )


type PresignedService struct { type PresignedService struct {
@@ -32,18 +33,18 @@ func (s *PresignedService) ObjectListByPath(ctx *gin.Context) {
var req cliapi.PresignedObjectListByPath var req cliapi.PresignedObjectListByPath
if err := ctx.ShouldBindQuery(&req); err != nil { if err := ctx.ShouldBindQuery(&req); err != nil {
log.Warnf("binding body: %s", err.Error()) log.Warnf("binding body: %s", err.Error())
ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
ctx.JSON(http.StatusBadRequest, types.Failed(ecode.BadArgument, "missing argument or invalid argument"))
return return
} }


resp, err := s.svc.ObjectSvc().GetByPath(req.ObjectListByPath) resp, err := s.svc.ObjectSvc().GetByPath(req.ObjectListByPath)
if err != nil { if err != nil {
log.Warnf("listing objects: %s", err.Error()) log.Warnf("listing objects: %s", err.Error())
ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, fmt.Sprintf("listing objects: %v", err)))
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, fmt.Sprintf("listing objects: %v", err)))
return return
} }


ctx.JSON(http.StatusOK, OK(resp))
ctx.JSON(http.StatusOK, types.OK(resp))
} }


func (s *PresignedService) ObjectDownloadByPath(ctx *gin.Context) { func (s *PresignedService) ObjectDownloadByPath(ctx *gin.Context) {
@@ -52,7 +53,7 @@ func (s *PresignedService) ObjectDownloadByPath(ctx *gin.Context) {
var req cliapi.PresignedObjectDownloadByPath var req cliapi.PresignedObjectDownloadByPath
if err := ctx.ShouldBindQuery(&req); err != nil { if err := ctx.ShouldBindQuery(&req); err != nil {
log.Warnf("binding query: %s", err.Error()) log.Warnf("binding query: %s", err.Error())
ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
ctx.JSON(http.StatusBadRequest, types.Failed(ecode.BadArgument, "missing argument or invalid argument"))
return return
} }


@@ -61,12 +62,12 @@ func (s *PresignedService) ObjectDownloadByPath(ctx *gin.Context) {
}) })
if err != nil { if err != nil {
log.Warnf("getting object by path: %s", err.Error()) log.Warnf("getting object by path: %s", err.Error())
ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "get object by path failed"))
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, "get object by path failed"))
return return
} }
if len(resp.Objects) == 0 { if len(resp.Objects) == 0 {
log.Warnf("object not found: %s", req.Path) log.Warnf("object not found: %s", req.Path)
ctx.JSON(http.StatusOK, Failed(errorcode.DataNotFound, "object not found"))
ctx.JSON(http.StatusOK, types.Failed(ecode.DataNotFound, "object not found"))
return return
} }


@@ -83,7 +84,7 @@ func (s *PresignedService) ObjectDownloadByPath(ctx *gin.Context) {
}) })
if err != nil { if err != nil {
log.Warnf("downloading object: %s", err.Error()) log.Warnf("downloading object: %s", err.Error())
ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "download object failed"))
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, "download object failed"))
return return
} }
defer file.File.Close() defer file.File.Close()
@@ -108,7 +109,7 @@ func (s *PresignedService) ObjectDownload(ctx *gin.Context) {
var req cliapi.PresignedObjectDownload var req cliapi.PresignedObjectDownload
if err := ctx.ShouldBindQuery(&req); err != nil { if err := ctx.ShouldBindQuery(&req); err != nil {
log.Warnf("binding query: %s", err.Error()) log.Warnf("binding query: %s", err.Error())
ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
ctx.JSON(http.StatusBadRequest, types.Failed(ecode.BadArgument, "missing argument or invalid argument"))
return return
} }


@@ -125,7 +126,7 @@ func (s *PresignedService) ObjectDownload(ctx *gin.Context) {
}) })
if err != nil { if err != nil {
log.Warnf("downloading object: %s", err.Error()) log.Warnf("downloading object: %s", err.Error())
ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "download object failed"))
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, "download object failed"))
return return
} }
defer file.File.Close() defer file.File.Close()
@@ -150,14 +151,14 @@ func (s *PresignedService) ObjectUpload(ctx *gin.Context) {
var req cliapi.PresignedObjectUpload var req cliapi.PresignedObjectUpload
if err := ctx.ShouldBindQuery(&req); err != nil { if err := ctx.ShouldBindQuery(&req); err != nil {
log.Warnf("binding query: %s", err.Error()) log.Warnf("binding query: %s", err.Error())
ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
ctx.JSON(http.StatusBadRequest, types.Failed(ecode.BadArgument, "missing argument or invalid argument"))
return return
} }


up, err := s.svc.Uploader.BeginUpdate(req.PackageID, req.Affinity, req.CopyTo, req.CopyToPath) up, err := s.svc.Uploader.BeginUpdate(req.PackageID, req.Affinity, req.CopyTo, req.CopyToPath)
if err != nil { if err != nil {
log.Warnf("begin update: %s", err.Error()) log.Warnf("begin update: %s", err.Error())
ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, fmt.Sprintf("begin update: %v", err)))
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, fmt.Sprintf("begin update: %v", err)))
return return
} }
defer up.Abort() defer up.Abort()
@@ -167,18 +168,18 @@ func (s *PresignedService) ObjectUpload(ctx *gin.Context) {
err = up.Upload(path, ctx.Request.Body) err = up.Upload(path, ctx.Request.Body)
if err != nil { if err != nil {
log.Warnf("uploading file: %s", err.Error()) log.Warnf("uploading file: %s", err.Error())
ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, fmt.Sprintf("uploading file %v: %v", req.Path, err)))
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, fmt.Sprintf("uploading file %v: %v", req.Path, err)))
return return
} }


ret, err := up.Commit() ret, err := up.Commit()
if err != nil { if err != nil {
log.Warnf("commit update: %s", err.Error()) log.Warnf("commit update: %s", err.Error())
ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, fmt.Sprintf("commit update: %v", err)))
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, fmt.Sprintf("commit update: %v", err)))
return return
} }


ctx.JSON(http.StatusOK, OK(cliapi.PresignedObjectUploadResp{Object: ret.Objects[path]}))
ctx.JSON(http.StatusOK, types.OK(cliapi.PresignedObjectUploadResp{Object: ret.Objects[path]}))
} }


func (s *PresignedService) ObjectNewMultipartUpload(ctx *gin.Context) { func (s *PresignedService) ObjectNewMultipartUpload(ctx *gin.Context) {
@@ -187,18 +188,18 @@ func (s *PresignedService) ObjectNewMultipartUpload(ctx *gin.Context) {
var req cliapi.PresignedObjectNewMultipartUpload var req cliapi.PresignedObjectNewMultipartUpload
if err := ctx.ShouldBindQuery(&req); err != nil { if err := ctx.ShouldBindQuery(&req); err != nil {
log.Warnf("binding query: %s", err.Error()) log.Warnf("binding query: %s", err.Error())
ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
ctx.JSON(http.StatusBadRequest, types.Failed(ecode.BadArgument, "missing argument or invalid argument"))
return return
} }


obj, err := s.svc.ObjectSvc().NewMultipartUploadObject(req.PackageID, req.Path) obj, err := s.svc.ObjectSvc().NewMultipartUploadObject(req.PackageID, req.Path)
if err != nil { if err != nil {
log.Warnf("new multipart upload: %s", err.Error()) log.Warnf("new multipart upload: %s", err.Error())
ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, fmt.Sprintf("new multipart upload: %v", err)))
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, fmt.Sprintf("new multipart upload: %v", err)))
return return
} }


ctx.JSON(http.StatusOK, OK(cliapi.PresignedObjectUploadResp{Object: obj}))
ctx.JSON(http.StatusOK, types.OK(cliapi.PresignedObjectUploadResp{Object: obj}))
} }


func (s *PresignedService) ObjectUploadPart(ctx *gin.Context) { func (s *PresignedService) ObjectUploadPart(ctx *gin.Context) {
@@ -207,18 +208,18 @@ func (s *PresignedService) ObjectUploadPart(ctx *gin.Context) {
var req cliapi.PresignedObjectUploadPart var req cliapi.PresignedObjectUploadPart
if err := ctx.ShouldBindQuery(&req); err != nil { if err := ctx.ShouldBindQuery(&req); err != nil {
log.Warnf("binding query: %s", err.Error()) log.Warnf("binding query: %s", err.Error())
ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
ctx.JSON(http.StatusBadRequest, types.Failed(ecode.BadArgument, "missing argument or invalid argument"))
return return
} }


err := s.svc.Uploader.UploadPart(req.ObjectID, req.Index, ctx.Request.Body) err := s.svc.Uploader.UploadPart(req.ObjectID, req.Index, ctx.Request.Body)
if err != nil { if err != nil {
log.Warnf("uploading part: %s", err.Error()) log.Warnf("uploading part: %s", err.Error())
ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, fmt.Sprintf("upload part: %v", err)))
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, fmt.Sprintf("upload part: %v", err)))
return return
} }


ctx.JSON(http.StatusOK, OK(cliapi.ObjectUploadPartResp{}))
ctx.JSON(http.StatusOK, types.OK(cliapi.ObjectUploadPartResp{}))
} }


func (s *PresignedService) ObjectCompleteMultipartUpload(ctx *gin.Context) { func (s *PresignedService) ObjectCompleteMultipartUpload(ctx *gin.Context) {
@@ -227,16 +228,16 @@ func (s *PresignedService) ObjectCompleteMultipartUpload(ctx *gin.Context) {
var req cliapi.PresignedObjectCompleteMultipartUpload var req cliapi.PresignedObjectCompleteMultipartUpload
if err := ctx.ShouldBindQuery(&req); err != nil { if err := ctx.ShouldBindQuery(&req); err != nil {
log.Warnf("binding query: %s", err.Error()) log.Warnf("binding query: %s", err.Error())
ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
ctx.JSON(http.StatusBadRequest, types.Failed(ecode.BadArgument, "missing argument or invalid argument"))
return return
} }


obj, err := s.svc.ObjectSvc().CompleteMultipartUpload(req.ObjectID, req.Indexes) obj, err := s.svc.ObjectSvc().CompleteMultipartUpload(req.ObjectID, req.Indexes)
if err != nil { if err != nil {
log.Warnf("completing multipart upload: %s", err.Error()) log.Warnf("completing multipart upload: %s", err.Error())
ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, fmt.Sprintf("complete multipart upload: %v", err)))
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, fmt.Sprintf("complete multipart upload: %v", err)))
return return
} }


ctx.JSON(http.StatusOK, OK(cliapi.ObjectCompleteMultipartUploadResp{Object: obj}))
ctx.JSON(http.StatusOK, types.OK(cliapi.ObjectCompleteMultipartUploadResp{Object: obj}))
} }

+ 47
- 45
client/internal/http/v1/server.go View File

@@ -2,6 +2,7 @@ package http


import ( import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"gitlink.org.cn/cloudream/jcs-pub/client/internal/http/auth"
"gitlink.org.cn/cloudream/jcs-pub/client/internal/http/types" "gitlink.org.cn/cloudream/jcs-pub/client/internal/http/types"
"gitlink.org.cn/cloudream/jcs-pub/client/internal/services" "gitlink.org.cn/cloudream/jcs-pub/client/internal/services"
cliapi "gitlink.org.cn/cloudream/jcs-pub/client/sdk/api/v1" cliapi "gitlink.org.cn/cloudream/jcs-pub/client/sdk/api/v1"
@@ -19,57 +20,58 @@ func NewServer(cfg *types.Config, svc *services.Service) *Server {
} }
} }


func (s *Server) InitRouters(rt gin.IRoutes) {
awsAuth := NewAWSAuth(s.cfg)
func (s *Server) InitRouters(rt gin.IRoutes, ah *auth.Auth) {
certAuth := ah.RejectNoCertAuth
signAuth := ah.Presigned


rt.GET(cliapi.ObjectListPathByPath, awsAuth.Auth, s.Object().ListByPath)
rt.GET(cliapi.ObjectListByIDsPath, awsAuth.Auth, s.Object().ListByIDs)
rt.GET(cliapi.ObjectDownloadPath, awsAuth.Auth, s.Object().Download)
rt.GET(cliapi.ObjectDownloadByPathPath, awsAuth.Auth, s.Object().DownloadByPath)
rt.POST(cliapi.ObjectUploadPath, awsAuth.AuthWithoutBody, s.Object().Upload)
rt.GET(cliapi.ObjectGetPackageObjectsPath, awsAuth.Auth, s.Object().GetPackageObjects)
rt.POST(cliapi.ObjectUpdateInfoPath, awsAuth.Auth, s.Object().UpdateInfo)
rt.POST(cliapi.ObjectUpdateInfoByPathPath, awsAuth.Auth, s.Object().UpdateInfoByPath)
rt.POST(cliapi.ObjectMovePath, awsAuth.Auth, s.Object().Move)
rt.POST(cliapi.ObjectDeletePath, awsAuth.Auth, s.Object().Delete)
rt.POST(cliapi.ObjectDeleteByPathPath, awsAuth.Auth, s.Object().DeleteByPath)
rt.POST(cliapi.ObjectClonePath, awsAuth.Auth, s.Object().Clone)
rt.GET(cliapi.ObjectListPathByPath, certAuth, s.Object().ListByPath)
rt.GET(cliapi.ObjectListByIDsPath, certAuth, s.Object().ListByIDs)
rt.GET(cliapi.ObjectDownloadPath, certAuth, s.Object().Download)
rt.GET(cliapi.ObjectDownloadByPathPath, certAuth, s.Object().DownloadByPath)
rt.POST(cliapi.ObjectUploadPath, certAuth, s.Object().Upload)
rt.GET(cliapi.ObjectGetPackageObjectsPath, certAuth, s.Object().GetPackageObjects)
rt.POST(cliapi.ObjectUpdateInfoPath, certAuth, s.Object().UpdateInfo)
rt.POST(cliapi.ObjectUpdateInfoByPathPath, certAuth, s.Object().UpdateInfoByPath)
rt.POST(cliapi.ObjectMovePath, certAuth, s.Object().Move)
rt.POST(cliapi.ObjectDeletePath, certAuth, s.Object().Delete)
rt.POST(cliapi.ObjectDeleteByPathPath, certAuth, s.Object().DeleteByPath)
rt.POST(cliapi.ObjectClonePath, certAuth, s.Object().Clone)


rt.GET(cliapi.PackageGetPath, awsAuth.Auth, s.Package().Get)
rt.GET(cliapi.PackageGetByFullNamePath, awsAuth.Auth, s.Package().GetByFullName)
rt.POST(cliapi.PackageCreatePath, awsAuth.Auth, s.Package().Create)
rt.POST(cliapi.PackageCreateUploadPath, awsAuth.Auth, s.Package().CreateLoad)
rt.POST(cliapi.PackageDeletePath, awsAuth.Auth, s.Package().Delete)
rt.POST(cliapi.PackageClonePath, awsAuth.Auth, s.Package().Clone)
rt.GET(cliapi.PackageListBucketPackagesPath, awsAuth.Auth, s.Package().ListBucketPackages)
rt.GET(cliapi.PackageGetPath, certAuth, s.Package().Get)
rt.GET(cliapi.PackageGetByFullNamePath, certAuth, s.Package().GetByFullName)
rt.POST(cliapi.PackageCreatePath, certAuth, s.Package().Create)
rt.POST(cliapi.PackageCreateUploadPath, certAuth, s.Package().CreateLoad)
rt.POST(cliapi.PackageDeletePath, certAuth, s.Package().Delete)
rt.POST(cliapi.PackageClonePath, certAuth, s.Package().Clone)
rt.GET(cliapi.PackageListBucketPackagesPath, certAuth, s.Package().ListBucketPackages)


rt.POST(cliapi.UserSpaceDownloadPackagePath, awsAuth.Auth, s.UserSpace().DownloadPackage)
rt.POST(cliapi.UserSpaceCreatePackagePath, awsAuth.Auth, s.UserSpace().CreatePackage)
rt.GET(cliapi.UserSpaceGetPath, awsAuth.Auth, s.UserSpace().Get)
rt.POST(cliapi.UserSpaceCreatePath, awsAuth.Auth, s.UserSpace().Create)
rt.POST(cliapi.UserSpaceUpdatePath, awsAuth.Auth, s.UserSpace().Update)
rt.POST(cliapi.UserSpaceDeletePath, awsAuth.Auth, s.UserSpace().Delete)
rt.POST(cliapi.UserSpaceTestPath, awsAuth.Auth, s.UserSpace().Test)
rt.POST(cliapi.UserSpaceSpaceToSpacePath, awsAuth.Auth, s.UserSpace().SpaceToSpace)
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.POST(cliapi.UserSpaceCreatePath, certAuth, s.UserSpace().Create)
rt.POST(cliapi.UserSpaceUpdatePath, certAuth, s.UserSpace().Update)
rt.POST(cliapi.UserSpaceDeletePath, certAuth, s.UserSpace().Delete)
rt.POST(cliapi.UserSpaceTestPath, certAuth, s.UserSpace().Test)
rt.POST(cliapi.UserSpaceSpaceToSpacePath, certAuth, s.UserSpace().SpaceToSpace)


rt.GET(cliapi.BucketGetByNamePath, awsAuth.Auth, s.Bucket().GetByName)
rt.POST(cliapi.BucketCreatePath, awsAuth.Auth, s.Bucket().Create)
rt.POST(cliapi.BucketDeletePath, awsAuth.Auth, s.Bucket().Delete)
rt.GET(cliapi.BucketListAllPath, awsAuth.Auth, s.Bucket().ListAll)
rt.GET(cliapi.BucketGetByNamePath, certAuth, s.Bucket().GetByName)
rt.POST(cliapi.BucketCreatePath, certAuth, s.Bucket().Create)
rt.POST(cliapi.BucketDeletePath, certAuth, s.Bucket().Delete)
rt.GET(cliapi.BucketListAllPath, certAuth, s.Bucket().ListAll)


rt.POST(cliapi.ObjectNewMultipartUploadPath, awsAuth.Auth, s.Object().NewMultipartUpload)
rt.POST(cliapi.ObjectUploadPartPath, awsAuth.AuthWithoutBody, s.Object().UploadPart)
rt.POST(cliapi.ObjectCompleteMultipartUploadPath, awsAuth.Auth, s.Object().CompleteMultipartUpload)
rt.POST(cliapi.ObjectNewMultipartUploadPath, certAuth, s.Object().NewMultipartUpload)
rt.POST(cliapi.ObjectUploadPartPath, certAuth, s.Object().UploadPart)
rt.POST(cliapi.ObjectCompleteMultipartUploadPath, certAuth, s.Object().CompleteMultipartUpload)


rt.GET(cliapi.PresignedObjectListByPathPath, awsAuth.PresignedAuth, s.Presigned().ObjectListByPath)
rt.GET(cliapi.PresignedObjectDownloadByPathPath, awsAuth.PresignedAuth, s.Presigned().ObjectDownloadByPath)
rt.GET(cliapi.PresignedObjectDownloadPath, awsAuth.PresignedAuth, s.Presigned().ObjectDownload)
rt.POST(cliapi.PresignedObjectUploadPath, awsAuth.PresignedAuth, s.Presigned().ObjectUpload)
rt.GET(cliapi.PresignedObjectListByPathPath, signAuth, s.Presigned().ObjectListByPath)
rt.GET(cliapi.PresignedObjectDownloadByPathPath, signAuth, s.Presigned().ObjectDownloadByPath)
rt.GET(cliapi.PresignedObjectDownloadPath, signAuth, s.Presigned().ObjectDownload)
rt.POST(cliapi.PresignedObjectUploadPath, signAuth, s.Presigned().ObjectUpload)


rt.POST(cliapi.PresignedObjectNewMultipartUploadPath, awsAuth.PresignedAuth, s.Presigned().ObjectNewMultipartUpload)
rt.POST(cliapi.PresignedObjectUploadPartPath, awsAuth.PresignedAuth, s.Presigned().ObjectUploadPart)
rt.POST(cliapi.PresignedObjectCompleteMultipartUploadPath, awsAuth.PresignedAuth, s.Presigned().ObjectCompleteMultipartUpload)
rt.POST(cliapi.PresignedObjectNewMultipartUploadPath, signAuth, s.Presigned().ObjectNewMultipartUpload)
rt.POST(cliapi.PresignedObjectUploadPartPath, signAuth, s.Presigned().ObjectUploadPart)
rt.POST(cliapi.PresignedObjectCompleteMultipartUploadPath, signAuth, s.Presigned().ObjectCompleteMultipartUpload)


rt.GET(cliapi.MountDumpStatusPath, awsAuth.Auth, s.Mount().DumpStatus)
rt.POST(cliapi.MountStartReclaimSpacePath, awsAuth.Auth, s.Mount().StartReclaimSpace)
rt.GET(cliapi.MountDumpStatusPath, certAuth, s.Mount().DumpStatus)
rt.POST(cliapi.MountStartReclaimSpacePath, certAuth, s.Mount().StartReclaimSpace)
} }

+ 29
- 28
client/internal/http/v1/user_space.go View File

@@ -5,9 +5,10 @@ import (
"net/http" "net/http"


"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"gitlink.org.cn/cloudream/common/consts/errorcode"
"gitlink.org.cn/cloudream/common/pkgs/logger" "gitlink.org.cn/cloudream/common/pkgs/logger"
"gitlink.org.cn/cloudream/jcs-pub/client/internal/http/types"
cliapi "gitlink.org.cn/cloudream/jcs-pub/client/sdk/api/v1" cliapi "gitlink.org.cn/cloudream/jcs-pub/client/sdk/api/v1"
"gitlink.org.cn/cloudream/jcs-pub/common/ecode"
) )


type UserSpaceService struct { type UserSpaceService struct {
@@ -26,18 +27,18 @@ func (s *UserSpaceService) DownloadPackage(ctx *gin.Context) {
var req cliapi.UserSpaceDownloadPackageReq var req cliapi.UserSpaceDownloadPackageReq
if err := ctx.ShouldBindJSON(&req); err != nil { if err := ctx.ShouldBindJSON(&req); err != nil {
log.Warnf("binding body: %s", err.Error()) log.Warnf("binding body: %s", err.Error())
ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
ctx.JSON(http.StatusBadRequest, types.Failed(ecode.BadArgument, "missing argument or invalid argument"))
return return
} }


err := s.svc.UserSpaceSvc().DownloadPackage(req.PackageID, req.UserSpaceID, req.RootPath) err := s.svc.UserSpaceSvc().DownloadPackage(req.PackageID, req.UserSpaceID, req.RootPath)
if err != nil { if err != nil {
log.Warnf("downloading package: %s", err.Error()) log.Warnf("downloading package: %s", err.Error())
ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "%v", err))
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, "%v", err))
return return
} }


ctx.JSON(http.StatusOK, OK(cliapi.UserSpaceDownloadPackageResp{}))
ctx.JSON(http.StatusOK, types.OK(cliapi.UserSpaceDownloadPackageResp{}))
} }


func (s *UserSpaceService) CreatePackage(ctx *gin.Context) { func (s *UserSpaceService) CreatePackage(ctx *gin.Context) {
@@ -46,18 +47,18 @@ func (s *UserSpaceService) CreatePackage(ctx *gin.Context) {
var req cliapi.UserSpaceCreatePackageReq var req cliapi.UserSpaceCreatePackageReq
if err := ctx.ShouldBindJSON(&req); err != nil { if err := ctx.ShouldBindJSON(&req); err != nil {
log.Warnf("binding body: %s", err.Error()) log.Warnf("binding body: %s", err.Error())
ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
ctx.JSON(http.StatusBadRequest, types.Failed(ecode.BadArgument, "missing argument or invalid argument"))
return return
} }


pkg, err := s.svc.Uploader.UserSpaceUpload(req.UserSpaceID, req.Path, req.BucketID, req.Name, req.SpaceAffinity) pkg, err := s.svc.Uploader.UserSpaceUpload(req.UserSpaceID, req.Path, req.BucketID, req.Name, req.SpaceAffinity)
if err != nil { if err != nil {
log.Warnf("userspace create package: %s", err.Error()) log.Warnf("userspace create package: %s", err.Error())
ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, fmt.Sprintf("userspace create package: %v", err)))
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, fmt.Sprintf("userspace create package: %v", err)))
return return
} }


ctx.JSON(http.StatusOK, OK(cliapi.UserSpaceCreatePackageResp{
ctx.JSON(http.StatusOK, types.OK(cliapi.UserSpaceCreatePackageResp{
Package: *pkg, Package: *pkg,
})) }))
} }
@@ -68,18 +69,18 @@ func (s *UserSpaceService) Get(ctx *gin.Context) {
var req cliapi.UserSpaceGet var req cliapi.UserSpaceGet
if err := ctx.ShouldBindQuery(&req); err != nil { if err := ctx.ShouldBindQuery(&req); err != nil {
log.Warnf("binding query: %s", err.Error()) log.Warnf("binding query: %s", err.Error())
ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
ctx.JSON(http.StatusBadRequest, types.Failed(ecode.BadArgument, "missing argument or invalid argument"))
return return
} }


info, err := s.svc.UserSpaceSvc().Get(req.UserSpaceID) info, err := s.svc.UserSpaceSvc().Get(req.UserSpaceID)
if err != nil { if err != nil {
log.Warnf("getting info: %s", err.Error()) log.Warnf("getting info: %s", err.Error())
ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "get userspace inf failed"))
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, "get userspace inf failed"))
return return
} }


ctx.JSON(http.StatusOK, OK(cliapi.UserSpaceGetResp{
ctx.JSON(http.StatusOK, types.OK(cliapi.UserSpaceGetResp{
UserSpace: info, UserSpace: info,
})) }))
} }
@@ -87,42 +88,42 @@ func (s *UserSpaceService) Get(ctx *gin.Context) {
func (s *UserSpaceService) Create(ctx *gin.Context) { func (s *UserSpaceService) Create(ctx *gin.Context) {
log := logger.WithField("HTTP", "UserSpace.Create") log := logger.WithField("HTTP", "UserSpace.Create")


req, err := ShouldBindJSONEx[cliapi.UserSpaceCreate](ctx)
req, err := types.ShouldBindJSONEx[cliapi.UserSpaceCreate](ctx)
if err != nil { if err != nil {
log.Warnf("binding body: %s", err.Error()) log.Warnf("binding body: %s", err.Error())
ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
ctx.JSON(http.StatusBadRequest, types.Failed(ecode.BadArgument, "missing argument or invalid argument"))
return return
} }


resp, cerr := s.svc.UserSpaceSvc().Create(req) resp, cerr := s.svc.UserSpaceSvc().Create(req)
if cerr != nil { if cerr != nil {
log.Warnf("creating userspace: %v", cerr) log.Warnf("creating userspace: %v", cerr)
ctx.JSON(http.StatusOK, Failed(string(cerr.Code), cerr.Message))
ctx.JSON(http.StatusOK, types.Failed(cerr.Code, cerr.Message))
return return
} }


ctx.JSON(http.StatusOK, OK(resp))
ctx.JSON(http.StatusOK, types.OK(resp))
} }


func (s *UserSpaceService) Update(ctx *gin.Context) { func (s *UserSpaceService) Update(ctx *gin.Context) {
log := logger.WithField("HTTP", "UserSpace.Update") log := logger.WithField("HTTP", "UserSpace.Update")


var req cliapi.UserSpaceUpdate var req cliapi.UserSpaceUpdate
req, err := ShouldBindJSONEx[cliapi.UserSpaceUpdate](ctx)
req, err := types.ShouldBindJSONEx[cliapi.UserSpaceUpdate](ctx)
if err != nil { if err != nil {
log.Warnf("binding body: %s", err.Error()) log.Warnf("binding body: %s", err.Error())
ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
ctx.JSON(http.StatusBadRequest, types.Failed(ecode.BadArgument, "missing argument or invalid argument"))
return return
} }


resp, cerr := s.svc.UserSpaceSvc().Update(req) resp, cerr := s.svc.UserSpaceSvc().Update(req)
if cerr != nil { if cerr != nil {
log.Warnf("updating userspace: %v", cerr) log.Warnf("updating userspace: %v", cerr)
ctx.JSON(http.StatusOK, Failed(string(cerr.Code), cerr.Message))
ctx.JSON(http.StatusOK, types.Failed(cerr.Code, cerr.Message))
return return
} }


ctx.JSON(http.StatusOK, OK(resp))
ctx.JSON(http.StatusOK, types.OK(resp))
} }


func (s *UserSpaceService) Delete(ctx *gin.Context) { func (s *UserSpaceService) Delete(ctx *gin.Context) {
@@ -131,39 +132,39 @@ func (s *UserSpaceService) Delete(ctx *gin.Context) {
var req cliapi.UserSpaceDelete var req cliapi.UserSpaceDelete
if err := ctx.ShouldBindJSON(&req); err != nil { if err := ctx.ShouldBindJSON(&req); err != nil {
log.Warnf("binding body: %s", err.Error()) log.Warnf("binding body: %s", err.Error())
ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
ctx.JSON(http.StatusBadRequest, types.Failed(ecode.BadArgument, "missing argument or invalid argument"))
return return
} }


resp, cerr := s.svc.UserSpaceSvc().Delete(req) resp, cerr := s.svc.UserSpaceSvc().Delete(req)
if cerr != nil { if cerr != nil {
log.Warnf("deleting userspace: %v", cerr) log.Warnf("deleting userspace: %v", cerr)
ctx.JSON(http.StatusOK, Failed(string(cerr.Code), cerr.Message))
ctx.JSON(http.StatusOK, types.Failed(cerr.Code, cerr.Message))
return return
} }


ctx.JSON(http.StatusOK, OK(resp))
ctx.JSON(http.StatusOK, types.OK(resp))
} }


func (s *UserSpaceService) Test(ctx *gin.Context) { func (s *UserSpaceService) Test(ctx *gin.Context) {
log := logger.WithField("HTTP", "UserSpace.Test") log := logger.WithField("HTTP", "UserSpace.Test")


var req cliapi.UserSpaceTest var req cliapi.UserSpaceTest
req, err := ShouldBindJSONEx[cliapi.UserSpaceTest](ctx)
req, err := types.ShouldBindJSONEx[cliapi.UserSpaceTest](ctx)
if err != nil { if err != nil {
log.Warnf("binding body: %s", err.Error()) log.Warnf("binding body: %s", err.Error())
ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
ctx.JSON(http.StatusBadRequest, types.Failed(ecode.BadArgument, "missing argument or invalid argument"))
return return
} }


resp, cerr := s.svc.UserSpaceSvc().Test(req) resp, cerr := s.svc.UserSpaceSvc().Test(req)
if cerr != nil { if cerr != nil {
log.Warnf("testing userspace: %v", cerr) log.Warnf("testing userspace: %v", cerr)
ctx.JSON(http.StatusOK, Failed(string(cerr.Code), cerr.Message))
ctx.JSON(http.StatusOK, types.Failed(cerr.Code, cerr.Message))
return return
} }


ctx.JSON(http.StatusOK, OK(resp))
ctx.JSON(http.StatusOK, types.OK(resp))
} }


func (s *UserSpaceService) SpaceToSpace(ctx *gin.Context) { func (s *UserSpaceService) SpaceToSpace(ctx *gin.Context) {
@@ -172,18 +173,18 @@ func (s *UserSpaceService) SpaceToSpace(ctx *gin.Context) {
var req cliapi.UserSpaceSpaceToSpace var req cliapi.UserSpaceSpaceToSpace
if err := ctx.ShouldBindJSON(&req); err != nil { if err := ctx.ShouldBindJSON(&req); err != nil {
log.Warnf("binding body: %s", err.Error()) log.Warnf("binding body: %s", err.Error())
ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
ctx.JSON(http.StatusBadRequest, types.Failed(ecode.BadArgument, "missing argument or invalid argument"))
return return
} }


ret, err := s.svc.UserSpaceSvc().SpaceToSpace(req.SrcUserSpaceID, req.SrcPath, req.DstUserSpaceID, req.DstPath) ret, err := s.svc.UserSpaceSvc().SpaceToSpace(req.SrcUserSpaceID, req.SrcPath, req.DstUserSpaceID, req.DstPath)
if err != nil { if err != nil {
log.Warnf("space2space: %s", err.Error()) log.Warnf("space2space: %s", err.Error())
ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "space2space failed"))
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, "space2space failed"))
return return
} }


ctx.JSON(http.StatusOK, OK(cliapi.UserSpaceSpaceToSpaceResp{
ctx.JSON(http.StatusOK, types.OK(cliapi.UserSpaceSpaceToSpaceResp{
SpaceToSpaceResult: ret, SpaceToSpaceResult: ret,
})) }))
} }

+ 150
- 0
client/sdk/signer/signer.go View File

@@ -0,0 +1,150 @@
package signer

import (
"crypto/ecdsa"
"crypto/rand"
"crypto/sha256"
"encoding/hex"
"fmt"
"net/url"
"strconv"
"strings"
"time"

"gitlink.org.cn/cloudream/common/utils/sort2"
)

var ErrSignatureMismatch = fmt.Errorf("signature mismatch")

var ErrExpired = fmt.Errorf("signature expired")

const (
DateQueryKey = "x-jcs-date"
ExpiresQueryKey = "x-jcs-expires"
SignatureQueryKey = "x-jcs-signature"
AccessKeyIDQueryKey = "x-jcs-access-key-id"
AlgorithmQueryKey = "x-jcs-algorithm"
AlgorithmValue = "ECDSA256"
DateFormat = "20060102T150405Z"
)

type SignKey struct {
privateKey *ecdsa.PrivateKey
accessKeyID string
}

func PresignURL(key *SignKey, method string, u *url.URL, signingTime time.Time, expiresSeconds int) error {
date := signingTime.Format(DateFormat)

queries := u.Query()
queries.Set(DateQueryKey, date)
queries.Set(ExpiresQueryKey, fmt.Sprintf("%v", expiresSeconds))
queries.Set(AccessKeyIDQueryKey, key.accessKeyID)
queries.Set(AlgorithmQueryKey, AlgorithmValue)

ss := makeStringToSign(method, u.Host, u.Path, queries)
ssHash := sha256.Sum256([]byte(ss))

sign, err := ecdsa.SignASN1(rand.Reader, key.privateKey, ssHash[:])
if err != nil {
return err
}

queries.Set(SignatureQueryKey, hex.EncodeToString(sign))
u.RawQuery = queries.Encode()
return nil
}

type VerifyKey struct {
publicKey *ecdsa.PublicKey
}

func NewVerifyKey(publicKey *ecdsa.PublicKey) *VerifyKey {
return &VerifyKey{publicKey: publicKey}
}

func GetAccessKeyID(u *url.URL) string {
return u.Query().Get(AccessKeyIDQueryKey)
}

func VerifyPresigned(key *VerifyKey, method string, u *url.URL) error {
queries := u.Query()

dateStr := queries.Get(DateQueryKey)
if dateStr == "" {
return fmt.Errorf("missing date in query string")
}

date, err := time.Parse(DateFormat, dateStr)
if err != nil {
return fmt.Errorf("invalid date format: %v", err)
}

expiresStr := queries.Get(ExpiresQueryKey)
if expiresStr == "" {
return fmt.Errorf("missing expires in query string")
}

expires, err := strconv.Atoi(expiresStr)
if err != nil {
return fmt.Errorf("invalid expires format: %v", err)
}

if time.Now().After(date.Add(time.Duration(expires) * time.Second)) {
return ErrExpired
}

signatureStr := queries.Get(SignatureQueryKey)
signature, err := hex.DecodeString(signatureStr)
if err != nil {
return fmt.Errorf("invalid signature format: %v", err)
}

queries.Del(SignatureQueryKey)
ss := makeStringToSign(method, u.Host, u.Path, queries)
ssHash := sha256.Sum256([]byte(ss))

if !ecdsa.VerifyASN1(key.publicKey, ssHash[:], signature) {
return ErrSignatureMismatch
}

return nil
}

func makeStringToSign(method string, host string, path string, queries url.Values) string {
type queryKv struct {
Key string
Values []string
}

var queryKvs []queryKv
for k, vs := range queries {
vs = sort2.SortAsc(vs)
queryKvs = append(queryKvs, queryKv{Key: k, Values: vs})
}

queryKvs = sort2.Sort(queryKvs, func(left, right queryKv) int {
return strings.Compare(left.Key, right.Key)
})

var str strings.Builder
str.WriteString(method)
str.WriteByte('\n')
str.WriteString(host)
str.WriteByte('\n')
str.WriteString(path)
str.WriteByte('\n')
for _, kv := range queryKvs {
str.WriteString(kv.Key)
str.WriteByte('=')

for i, v := range kv.Values {
if i > 0 {
str.WriteByte(',')
}
str.WriteString(v)
}
str.WriteByte('\n')
}
return str.String()
}

+ 1
- 0
common/pkgs/storage/obs/obs.go View File

@@ -79,6 +79,7 @@ func createClient(stgType *cortypes.OBSType, cred *cortypes.OBSCred) (*s3.Client
} }
awsConfig.Credentials = &credentials.StaticCredentialsProvider{Value: cre} awsConfig.Credentials = &credentials.StaticCredentialsProvider{Value: cre}
awsConfig.Region = stgType.Region awsConfig.Region = stgType.Region
awsConfig.RetryMaxAttempts = 1


options := []func(*s3.Options){} options := []func(*s3.Options){}
options = append(options, func(s3Opt *s3.Options) { options = append(options, func(s3Opt *s3.Options) {


+ 16
- 9
coordinator/internal/cmd/cert.go View File

@@ -1,8 +1,9 @@
package cmd package cmd


import ( import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand" "crypto/rand"
"crypto/rsa"
"crypto/x509" "crypto/x509"
"crypto/x509/pkix" "crypto/x509/pkix"
"encoding/pem" "encoding/pem"
@@ -58,7 +59,7 @@ func init() {
} }


func certRoot(output string) { func certRoot(output string) {
caPriv, _ := rsa.GenerateKey(rand.Reader, 2048)
caPriv, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)


// 创建 CA 证书模板 // 创建 CA 证书模板
caTemplate := &x509.Certificate{ caTemplate := &x509.Certificate{
@@ -78,7 +79,9 @@ func certRoot(output string) {


// 保存 CA 证书和私钥 // 保存 CA 证书和私钥
writePem(filepath.Join(output, "ca_cert.pem"), "CERTIFICATE", caCertDER) writePem(filepath.Join(output, "ca_cert.pem"), "CERTIFICATE", caCertDER)
writePem(filepath.Join(output, "ca_key.pem"), "RSA PRIVATE KEY", x509.MarshalPKCS1PrivateKey(caPriv))

privPem, _ := x509.MarshalECPrivateKey(caPriv)
writePem(filepath.Join(output, "ca_key.pem"), "EC PRIVATE KEY", privPem)
fmt.Println("CA certificate and key saved to", output) fmt.Println("CA certificate and key saved to", output)
} }


@@ -111,14 +114,14 @@ func certServer(certFile string, keyFile string, output string) {
return return
} }


caKey, err := x509.ParsePKCS1PrivateKey(caKeyPEMBlock.Bytes)
caKey, err := x509.ParseECPrivateKey(caKeyPEMBlock.Bytes)
if err != nil { if err != nil {
fmt.Println("Failed to parse CA key:", err) fmt.Println("Failed to parse CA key:", err)
return return
} }


// 生成服务端私钥 // 生成服务端私钥
serverPriv, _ := rsa.GenerateKey(rand.Reader, 2048)
serverPriv, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)


// 服务端证书模板 // 服务端证书模板
serverTemplate := &x509.Certificate{ serverTemplate := &x509.Certificate{
@@ -141,7 +144,9 @@ func certServer(certFile string, keyFile string, output string) {


// 保存服务端证书和私钥 // 保存服务端证书和私钥
writePem(filepath.Join(output, "server_cert.pem"), "CERTIFICATE", serverCertDER) writePem(filepath.Join(output, "server_cert.pem"), "CERTIFICATE", serverCertDER)
writePem(filepath.Join(output, "server_key.pem"), "RSA PRIVATE KEY", x509.MarshalPKCS1PrivateKey(serverPriv))

privPem, _ := x509.MarshalECPrivateKey(serverPriv)
writePem(filepath.Join(output, "server_key.pem"), "EC PRIVATE KEY", privPem)
fmt.Println("Server certificate and key saved to", output) fmt.Println("Server certificate and key saved to", output)
} }


@@ -174,14 +179,14 @@ func certClient(certFile string, keyFile string, output string) {
return return
} }


caKey, err := x509.ParsePKCS1PrivateKey(caKeyPEMBlock.Bytes)
caKey, err := x509.ParseECPrivateKey(caKeyPEMBlock.Bytes)
if err != nil { if err != nil {
fmt.Println("Failed to parse CA key:", err) fmt.Println("Failed to parse CA key:", err)
return return
} }


// 生成客户端私钥 // 生成客户端私钥
clientPriv, _ := rsa.GenerateKey(rand.Reader, 2048)
clientPriv, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)


// 客户端证书模板 // 客户端证书模板
clientTemplate := &x509.Certificate{ clientTemplate := &x509.Certificate{
@@ -201,7 +206,9 @@ func certClient(certFile string, keyFile string, output string) {


// 保存客户端证书和私钥 // 保存客户端证书和私钥
writePem(filepath.Join(output, "client_cert.pem"), "CERTIFICATE", clientCertDER) writePem(filepath.Join(output, "client_cert.pem"), "CERTIFICATE", clientCertDER)
writePem(filepath.Join(output, "client_key.pem"), "RSA PRIVATE KEY", x509.MarshalPKCS1PrivateKey(clientPriv))

privPem, _ := x509.MarshalECPrivateKey(clientPriv)
writePem(filepath.Join(output, "client_key.pem"), "EC PRIVATE KEY", privPem)
fmt.Println("Client certificate and key saved to", output) fmt.Println("Client certificate and key saved to", output)
} }




Loading…
Cancel
Save