From 15a9bb1642fb888414b8f10a20f4eb6f5f4893c9 Mon Sep 17 00:00:00 2001 From: Sydonian <794346190@qq.com> Date: Mon, 9 Jun 2025 10:51:56 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=A2=E6=88=B7=E7=AB=AF=E9=89=B4=E6=9D=83?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=E8=AF=81=E4=B9=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/internal/cmdline/cert.go | 219 ++++++++++++++++++++ client/internal/cmdline/serve.go | 15 +- client/internal/cmdline/vfstest.go | 15 +- client/internal/config/config.go | 2 +- client/internal/http/auth/auth.go | 80 +++++++ client/internal/http/http.go | 2 +- client/internal/http/server.go | 19 +- client/internal/http/types/config.go | 111 +++++++++- client/internal/http/types/types.go | 1 + client/internal/http/{v1 => types}/utils.go | 10 +- client/internal/http/v1/aws_auth.go | 50 ++--- client/internal/http/v1/bucket.go | 27 +-- client/internal/http/v1/mount.go | 11 +- client/internal/http/v1/object.go | 113 +++++----- client/internal/http/v1/package.go | 55 ++--- client/internal/http/v1/presigned.go | 49 ++--- client/internal/http/v1/server.go | 92 ++++---- client/internal/http/v1/user_space.go | 57 ++--- client/sdk/signer/signer.go | 150 ++++++++++++++ common/pkgs/storage/obs/obs.go | 1 + coordinator/internal/cmd/cert.go | 25 ++- 21 files changed, 845 insertions(+), 259 deletions(-) create mode 100644 client/internal/cmdline/cert.go create mode 100644 client/internal/http/auth/auth.go create mode 100644 client/internal/http/types/types.go rename client/internal/http/{v1 => types}/utils.go (77%) create mode 100644 client/sdk/signer/signer.go diff --git a/client/internal/cmdline/cert.go b/client/internal/cmdline/cert.go new file mode 100644 index 0000000..024dfe2 --- /dev/null +++ b/client/internal/cmdline/cert.go @@ -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() +} diff --git a/client/internal/cmdline/serve.go b/client/internal/cmdline/serve.go index 6a8e721..fd4259d 100644 --- a/client/internal/cmdline/serve.go +++ b/client/internal/cmdline/serve.go @@ -192,13 +192,20 @@ func serveHTTP(configPath string, opts serveHTTPOptions) { svc := services.NewService(publock, dlder, acStat, uploader, strgSel, stgMeta, db, evtPub, mnt, stgPool) // 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 != "" { - httpCfg.Listen = opts.HTTPListenAddr + httpCfgJSON.Listen = opts.HTTPListenAddr } } 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) httpChan := httpSvr.Start() diff --git a/client/internal/cmdline/vfstest.go b/client/internal/cmdline/vfstest.go index 140d696..3336c26 100644 --- a/client/internal/cmdline/vfstest.go +++ b/client/internal/cmdline/vfstest.go @@ -174,13 +174,20 @@ func vfsTest(configPath string, opts serveHTTPOptions) { svc := services.NewService(publock, dlder, acStat, uploader, strgSel, stgMeta, db, evtPub, mnt, stgPool) // 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 != "" { - httpCfg.Listen = opts.HTTPListenAddr + httpCfgJSON.Listen = opts.HTTPListenAddr } } 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) httpChan := httpSvr.Start() diff --git a/client/internal/config/config.go b/client/internal/config/config.go index 7fdd693..79eb990 100644 --- a/client/internal/config/config.go +++ b/client/internal/config/config.go @@ -28,7 +28,7 @@ type Config struct { Downloader downloader.Config `json:"downloader"` DownloadStrategy strategy.Config `json:"downloadStrategy"` TickTock ticktock.Config `json:"tickTock"` - HTTP *http.Config `json:"http"` + HTTP *http.ConfigJSON `json:"http"` Mount *mntcfg.Config `json:"mount"` AccessToken *accesstoken.Config `json:"accessToken"` } diff --git a/client/internal/http/auth/auth.go b/client/internal/http/auth/auth.go new file mode 100644 index 0000000..324da75 --- /dev/null +++ b/client/internal/http/auth/auth.go @@ -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() +} diff --git a/client/internal/http/http.go b/client/internal/http/http.go index 1ff8e80..dee5430 100644 --- a/client/internal/http/http.go +++ b/client/internal/http/http.go @@ -2,4 +2,4 @@ package http import "gitlink.org.cn/cloudream/jcs-pub/client/internal/http/types" -type Config = types.Config +type ConfigJSON = types.ConfigJSON diff --git a/client/internal/http/server.go b/client/internal/http/server.go index 3daa6ab..8c11c6a 100644 --- a/client/internal/http/server.go +++ b/client/internal/http/server.go @@ -2,11 +2,13 @@ package http import ( "context" + "crypto/tls" "net/http" "github.com/gin-gonic/gin" "gitlink.org.cn/cloudream/common/pkgs/async" "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" v1 "gitlink.org.cn/cloudream/jcs-pub/client/internal/http/v1" "gitlink.org.cn/cloudream/jcs-pub/client/internal/services" @@ -24,25 +26,26 @@ type ExitEvent struct { } type Server struct { - cfg *types.Config + cfg types.Config httpSrv *http.Server svc *services.Service eventChan *ServerEventChan + auth *auth.Auth v1Svr *v1.Server } -func NewServer(cfg *types.Config, svc *services.Service) *Server { +func NewServer(cfg types.Config, svc *services.Service) *Server { return &Server{ cfg: cfg, svc: svc, eventChan: async.NewUnboundChannel[ServerEvent](), - v1Svr: v1.NewServer(cfg, svc), + v1Svr: v1.NewServer(&cfg, svc), } } func (s *Server) Start() *ServerEventChan { go func() { - if s.cfg == nil { + if !s.cfg.Enabled { return } @@ -51,12 +54,16 @@ func (s *Server) Start() *ServerEventChan { Addr: s.cfg.Listen, 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) - err := s.httpSrv.ListenAndServe() + err := s.httpSrv.ListenAndServeTLS("", "") s.eventChan.Send(ExitEvent{Err: err}) }() return s.eventChan diff --git a/client/internal/http/types/config.go b/client/internal/http/types/config.go index 213eb65..5e94f2f 100644 --- a/client/internal/http/types/config.go +++ b/client/internal/http/types/config.go @@ -1,12 +1,109 @@ 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 { - 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 } diff --git a/client/internal/http/types/types.go b/client/internal/http/types/types.go new file mode 100644 index 0000000..ab1254f --- /dev/null +++ b/client/internal/http/types/types.go @@ -0,0 +1 @@ +package types diff --git a/client/internal/http/v1/utils.go b/client/internal/http/types/utils.go similarity index 77% rename from client/internal/http/v1/utils.go rename to client/internal/http/types/utils.go index 966a1b4..343f556 100644 --- a/client/internal/http/v1/utils.go +++ b/client/internal/http/types/utils.go @@ -1,12 +1,12 @@ -package http +package types import ( "fmt" "github.com/gin-gonic/gin" "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/jcs-pub/common/ecode" ) 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{ - Code: code, + Code: string(code), Message: fmt.Sprintf(format, args...), } } 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) } diff --git a/client/internal/http/v1/aws_auth.go b/client/internal/http/v1/aws_auth.go index ae3f7ad..f542361 100644 --- a/client/internal/http/v1/aws_auth.go +++ b/client/internal/http/v1/aws_auth.go @@ -1,5 +1,6 @@ package http +/* import ( "bytes" "context" @@ -28,12 +29,12 @@ const ( ) type AWSAuth struct { - cfg *types.Config + cfg *types.ConfigJSON cred aws.Credentials signer *v4.Signer } -func NewAWSAuth(cfg *types.Config) *AWSAuth { +func NewAWSAuth(cfg *types.ConfigJSON) *AWSAuth { auth := &AWSAuth{ cfg: cfg, } @@ -56,13 +57,13 @@ func (a *AWSAuth) Auth(c *gin.Context) { authorizationHeader := c.GetHeader(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 } _, headers, reqSig, err := parseAuthorizationHeader(authorizationHeader) 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 } @@ -70,18 +71,18 @@ func (a *AWSAuth) Auth(c *gin.Context) { rd := io.LimitReader(c.Request.Body, a.cfg.MaxBodySize) body, err := io.ReadAll(rd) 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 } timestamp, err := time.Parse("20060102T150405Z", c.GetHeader("X-Amz-Date")) 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 } 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 } @@ -91,7 +92,7 @@ func (a *AWSAuth) Auth(c *gin.Context) { // 构造验签用的请求 verifyReq, err := http.NewRequest(c.Request.Method, c.Request.URL.String(), nil) if err != nil { - c.AbortWithStatusJSON(http.StatusOK, Failed(errorcode.OperationFailed, err.Error())) + c.AbortWithStatusJSON(http.StatusOK, types.Failed(ecode.OperationFailed, err.Error())) return } 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) if err != nil { 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 } verifySig := getSignatureFromAWSHeader(verifyReq) if !strings.EqualFold(verifySig, reqSig) { 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 } @@ -132,31 +133,31 @@ func (a *AWSAuth) AuthWithoutBody(c *gin.Context) { authorizationHeader := c.GetHeader(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 } _, headers, reqSig, err := parseAuthorizationHeader(authorizationHeader) 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 } timestamp, err := time.Parse("20060102T150405Z", c.GetHeader("X-Amz-Date")) 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 } 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 } // 构造验签用的请求 verifyReq, err := http.NewRequest(c.Request.Method, c.Request.URL.String(), nil) if err != nil { - c.AbortWithStatusJSON(http.StatusOK, Failed(errorcode.OperationFailed, err.Error())) + c.AbortWithStatusJSON(http.StatusOK, types.Failed(ecode.OperationFailed, err.Error())) return } for _, h := range headers { @@ -173,14 +174,14 @@ func (a *AWSAuth) AuthWithoutBody(c *gin.Context) { if err != nil { 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 } verifySig := getSignatureFromAWSHeader(verifyReq) if !strings.EqualFold(verifySig, reqSig) { 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 } @@ -198,7 +199,7 @@ func (a *AWSAuth) PresignedAuth(c *gin.Context) { signature := query.Get("X-Amz-Signature") query.Del("X-Amz-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 } @@ -209,7 +210,7 @@ func (a *AWSAuth) PresignedAuth(c *gin.Context) { expiresStr := query.Get("X-Expires") expires, err := strconv.ParseInt(expiresStr, 10, 64) 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 } @@ -219,7 +220,7 @@ func (a *AWSAuth) PresignedAuth(c *gin.Context) { verifyReq, err := http.NewRequest(c.Request.Method, c.Request.URL.String(), nil) if err != nil { - c.AbortWithStatusJSON(http.StatusOK, Failed(errorcode.OperationFailed, err.Error())) + c.AbortWithStatusJSON(http.StatusOK, types.Failed(ecode.OperationFailed, err.Error())) return } for _, h := range signedHeaders { @@ -234,26 +235,26 @@ func (a *AWSAuth) PresignedAuth(c *gin.Context) { timestamp, err := time.Parse("20060102T150405Z", date) 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 } 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 } signer := v4.NewSigner() uri, _, err := signer.PresignHTTP(context.TODO(), a.cred, verifyReq, "", AuthService, AuthRegion, timestamp) 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 } verifySig := getSignatureFromAWSQuery(uri) if !strings.EqualFold(verifySig, signature) { 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 } @@ -319,3 +320,4 @@ func getSignatureFromAWSQuery(uri string) string { return uri[idx+len("X-Amz-Signature=") : andIdx] } +*/ diff --git a/client/internal/http/v1/bucket.go b/client/internal/http/v1/bucket.go index 3257246..c9fc6af 100644 --- a/client/internal/http/v1/bucket.go +++ b/client/internal/http/v1/bucket.go @@ -6,9 +6,10 @@ import ( "time" "github.com/gin-gonic/gin" - "gitlink.org.cn/cloudream/common/consts/errorcode" "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" + "gitlink.org.cn/cloudream/jcs-pub/common/ecode" ) type BucketService struct { @@ -27,18 +28,18 @@ func (s *BucketService) GetByName(ctx *gin.Context) { var req cliapi.BucketGetByName if err := ctx.ShouldBindQuery(&req); err != nil { 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 } bucket, err := s.svc.BucketSvc().GetBucketByName(req.Name) if err != nil { log.Warnf("getting bucket by name: %s", err.Error()) - ctx.JSON(http.StatusOK, FailedError(err)) + ctx.JSON(http.StatusOK, types.FailedError(err)) return } - ctx.JSON(http.StatusOK, OK(cliapi.BucketGetByNameResp{ + ctx.JSON(http.StatusOK, types.OK(cliapi.BucketGetByNameResp{ Bucket: bucket, })) } @@ -49,18 +50,18 @@ func (s *BucketService) Create(ctx *gin.Context) { var req cliapi.BucketCreate if err := ctx.ShouldBindJSON(&req); err != nil { 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 } bucket, err := s.svc.BucketSvc().CreateBucket(req.Name, time.Now()) if err != nil { log.Warnf("creating bucket: %s", err.Error()) - ctx.JSON(http.StatusOK, FailedError(err)) + ctx.JSON(http.StatusOK, types.FailedError(err)) return } - ctx.JSON(http.StatusOK, OK(cliapi.BucketCreateResp{ + ctx.JSON(http.StatusOK, types.OK(cliapi.BucketCreateResp{ Bucket: bucket, })) } @@ -71,17 +72,17 @@ func (s *BucketService) Delete(ctx *gin.Context) { var req cliapi.BucketDelete if err := ctx.ShouldBindJSON(&req); err != nil { 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 } if err := s.svc.BucketSvc().DeleteBucket(req.BucketID); err != nil { 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 } - ctx.JSON(http.StatusOK, OK(nil)) + ctx.JSON(http.StatusOK, types.OK(nil)) } func (s *BucketService) ListAll(ctx *gin.Context) { @@ -90,18 +91,18 @@ func (s *BucketService) ListAll(ctx *gin.Context) { var req cliapi.BucketListAll if err := ctx.ShouldBindQuery(&req); err != nil { 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 } buckets, err := s.svc.BucketSvc().ListAllBuckets() if err != nil { 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 } - ctx.JSON(http.StatusOK, OK(cliapi.BucketListAllResp{ + ctx.JSON(http.StatusOK, types.OK(cliapi.BucketListAllResp{ Buckets: buckets, })) } diff --git a/client/internal/http/v1/mount.go b/client/internal/http/v1/mount.go index 3426230..06e5dbf 100644 --- a/client/internal/http/v1/mount.go +++ b/client/internal/http/v1/mount.go @@ -4,9 +4,10 @@ import ( "net/http" "github.com/gin-gonic/gin" - "gitlink.org.cn/cloudream/common/consts/errorcode" "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" + "gitlink.org.cn/cloudream/jcs-pub/common/ecode" ) type MountService struct { @@ -25,12 +26,12 @@ func (m *MountService) DumpStatus(ctx *gin.Context) { var req cliapi.MountDumpStatus if err := ctx.ShouldBindQuery(&req); err != nil { 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 } dumpStatus := m.svc.Mount.Dump() - ctx.JSON(http.StatusOK, OK(cliapi.MountDumpStatusResp{ + ctx.JSON(http.StatusOK, types.OK(cliapi.MountDumpStatusResp{ MountStatus: dumpStatus, })) } @@ -40,10 +41,10 @@ func (m *MountService) StartReclaimSpace(ctx *gin.Context) { // var req cliapi.MountReclaimSpace // if err := ctx.ShouldBindJSON(&req); err != nil { // 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 // } m.svc.Mount.StartReclaimSpace() - ctx.JSON(http.StatusOK, OK(cliapi.StartMountReclaimSpaceResp{})) + ctx.JSON(http.StatusOK, types.OK(cliapi.StartMountReclaimSpaceResp{})) } diff --git a/client/internal/http/v1/object.go b/client/internal/http/v1/object.go index 03e4f1b..a87fc18 100644 --- a/client/internal/http/v1/object.go +++ b/client/internal/http/v1/object.go @@ -10,12 +10,13 @@ import ( "path/filepath" "github.com/gin-gonic/gin" - "gitlink.org.cn/cloudream/common/consts/errorcode" "gitlink.org.cn/cloudream/common/pkgs/logger" "gitlink.org.cn/cloudream/common/utils/math2" "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" clitypes "gitlink.org.cn/cloudream/jcs-pub/client/types" + "gitlink.org.cn/cloudream/jcs-pub/common/ecode" ) type ObjectService struct { @@ -34,18 +35,18 @@ func (s *ObjectService) ListByPath(ctx *gin.Context) { var req cliapi.ObjectListByPath if err := ctx.ShouldBindQuery(&req); err != nil { 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 } resp, err := s.svc.ObjectSvc().GetByPath(req) if err != nil { 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 } - ctx.JSON(http.StatusOK, OK(resp)) + ctx.JSON(http.StatusOK, types.OK(resp)) } func (s *ObjectService) ListByIDs(ctx *gin.Context) { @@ -54,18 +55,18 @@ func (s *ObjectService) ListByIDs(ctx *gin.Context) { var req cliapi.ObjectListByIDs if err := ctx.ShouldBindQuery(&req); err != nil { 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 } objs, err := s.svc.ObjectSvc().GetByIDs(req.ObjectIDs) if err != nil { 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 } - ctx.JSON(http.StatusOK, OK(cliapi.ObjectListByIDsResp{Objects: objs})) + ctx.JSON(http.StatusOK, types.OK(cliapi.ObjectListByIDsResp{Objects: objs})) } type ObjectUploadReq struct { @@ -79,14 +80,14 @@ func (s *ObjectService) Upload(ctx *gin.Context) { var req ObjectUploadReq if err := ctx.ShouldBind(&req); err != nil { 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 } up, err := s.svc.Uploader.BeginUpdate(req.Info.PackageID, req.Info.Affinity, req.Info.CopyTo, req.Info.CopyToPath) if err != nil { 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 } defer up.Abort() @@ -96,14 +97,14 @@ func (s *ObjectService) Upload(ctx *gin.Context) { f, err := file.Open() if err != nil { 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 } path, err := url.PathUnescape(file.Filename) if err != nil { 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 } path = filepath.ToSlash(path) @@ -111,7 +112,7 @@ func (s *ObjectService) Upload(ctx *gin.Context) { err = up.Upload(path, f) if err != nil { 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 } pathes = append(pathes, path) @@ -120,7 +121,7 @@ func (s *ObjectService) Upload(ctx *gin.Context) { ret, err := up.Commit() if err != nil { 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 } @@ -129,7 +130,7 @@ func (s *ObjectService) Upload(ctx *gin.Context) { 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) { @@ -138,7 +139,7 @@ func (s *ObjectService) Download(ctx *gin.Context) { var req cliapi.ObjectDownload if err := ctx.ShouldBindQuery(&req); err != nil { 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 } @@ -155,12 +156,12 @@ func (s *ObjectService) Download(ctx *gin.Context) { }) if err != nil { 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 } if file.File == nil { 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 } defer file.File.Close() @@ -186,7 +187,7 @@ func (s *ObjectService) DownloadByPath(ctx *gin.Context) { var req cliapi.ObjectDownloadByPath if err := ctx.ShouldBindQuery(&req); err != nil { 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 } @@ -195,13 +196,13 @@ func (s *ObjectService) DownloadByPath(ctx *gin.Context) { }) if err != nil { 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 } if len(resp.Objects) == 0 { 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 } @@ -218,7 +219,7 @@ func (s *ObjectService) DownloadByPath(ctx *gin.Context) { }) if err != nil { 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 } defer file.File.Close() @@ -243,18 +244,18 @@ func (s *ObjectService) UpdateInfo(ctx *gin.Context) { var req cliapi.ObjectUpdateInfo if err := ctx.ShouldBindJSON(&req); err != nil { 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 } sucs, err := s.svc.ObjectSvc().UpdateInfo(req.Updatings) if err != nil { 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 } - 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) { @@ -263,7 +264,7 @@ func (s *ObjectService) UpdateInfoByPath(ctx *gin.Context) { var req cliapi.ObjectUpdateInfoByPath if err := ctx.ShouldBindJSON(&req); err != nil { 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 } @@ -272,12 +273,12 @@ func (s *ObjectService) UpdateInfoByPath(ctx *gin.Context) { }) if err != nil { 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 } if len(resp.Objects) == 0 { 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 } @@ -287,13 +288,13 @@ func (s *ObjectService) UpdateInfoByPath(ctx *gin.Context) { }}) if err != nil { 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 } 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) { @@ -302,18 +303,18 @@ func (s *ObjectService) Move(ctx *gin.Context) { var req cliapi.ObjectMove if err := ctx.ShouldBindJSON(&req); err != nil { 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 } sucs, err := s.svc.ObjectSvc().Move(req.Movings) if err != nil { 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 } - 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) { @@ -322,18 +323,18 @@ func (s *ObjectService) Delete(ctx *gin.Context) { var req cliapi.ObjectDelete if err := ctx.ShouldBindJSON(&req); err != nil { 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 } err := s.svc.ObjectSvc().Delete(req.ObjectIDs) if err != nil { 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 } - ctx.JSON(http.StatusOK, OK(nil)) + ctx.JSON(http.StatusOK, types.OK(nil)) } func (s *ObjectService) DeleteByPath(ctx *gin.Context) { @@ -342,7 +343,7 @@ func (s *ObjectService) DeleteByPath(ctx *gin.Context) { var req cliapi.ObjectDeleteByPath if err := ctx.ShouldBindJSON(&req); err != nil { 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 } @@ -351,22 +352,22 @@ func (s *ObjectService) DeleteByPath(ctx *gin.Context) { }) if err != nil { 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 } if len(resp.Objects) == 0 { - ctx.JSON(http.StatusOK, OK(nil)) + ctx.JSON(http.StatusOK, types.OK(nil)) return } err = s.svc.ObjectSvc().Delete([]clitypes.ObjectID{resp.Objects[0].ObjectID}) if err != nil { 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 } - ctx.JSON(http.StatusOK, OK(nil)) + ctx.JSON(http.StatusOK, types.OK(nil)) } func (s *ObjectService) Clone(ctx *gin.Context) { @@ -375,18 +376,18 @@ func (s *ObjectService) Clone(ctx *gin.Context) { var req cliapi.ObjectClone if err := ctx.ShouldBindJSON(&req); err != nil { 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 } objs, err := s.svc.ObjectSvc().Clone(req.Clonings) if err != nil { 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 } - 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) { @@ -395,18 +396,18 @@ func (s *ObjectService) GetPackageObjects(ctx *gin.Context) { var req cliapi.ObjectGetPackageObjects if err := ctx.ShouldBindQuery(&req); err != nil { 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 } objs, err := s.svc.ObjectSvc().GetPackageObjects(req.PackageID) if err != nil { 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 } - 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) { @@ -415,18 +416,18 @@ func (s *ObjectService) NewMultipartUpload(ctx *gin.Context) { var req cliapi.ObjectNewMultipartUpload if err := ctx.ShouldBindJSON(&req); err != nil { 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 } obj, err := s.svc.ObjectSvc().NewMultipartUploadObject(req.PackageID, req.Path) if err != nil { 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 } - ctx.JSON(http.StatusOK, OK(cliapi.ObjectNewMultipartUploadResp{Object: obj})) + ctx.JSON(http.StatusOK, types.OK(cliapi.ObjectNewMultipartUploadResp{Object: obj})) } type ObjectUploadPartReq struct { @@ -440,14 +441,14 @@ func (s *ObjectService) UploadPart(ctx *gin.Context) { var req ObjectUploadPartReq if err := ctx.ShouldBind(&req); err != nil { 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 } file, err := req.File.Open() if err != nil { 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 } 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) if err != nil { 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 } - ctx.JSON(http.StatusOK, OK(cliapi.ObjectUploadPartResp{})) + ctx.JSON(http.StatusOK, types.OK(cliapi.ObjectUploadPartResp{})) } func (s *ObjectService) CompleteMultipartUpload(ctx *gin.Context) { @@ -468,16 +469,16 @@ func (s *ObjectService) CompleteMultipartUpload(ctx *gin.Context) { var req cliapi.ObjectCompleteMultipartUpload if err := ctx.ShouldBindJSON(&req); err != nil { 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 } obj, err := s.svc.ObjectSvc().CompleteMultipartUpload(req.ObjectID, req.Indexes) if err != nil { 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 } - ctx.JSON(http.StatusOK, OK(cliapi.ObjectCompleteMultipartUploadResp{Object: obj})) + ctx.JSON(http.StatusOK, types.OK(cliapi.ObjectCompleteMultipartUploadResp{Object: obj})) } diff --git a/client/internal/http/v1/package.go b/client/internal/http/v1/package.go index 28d598d..a390db8 100644 --- a/client/internal/http/v1/package.go +++ b/client/internal/http/v1/package.go @@ -8,10 +8,11 @@ import ( "path/filepath" "github.com/gin-gonic/gin" - "gitlink.org.cn/cloudream/common/consts/errorcode" "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" clitypes "gitlink.org.cn/cloudream/jcs-pub/client/types" + "gitlink.org.cn/cloudream/jcs-pub/common/ecode" ) // PackageService 包服务,负责处理包相关的HTTP请求。 @@ -32,18 +33,18 @@ func (s *PackageService) Get(ctx *gin.Context) { var req cliapi.PackageGetReq if err := ctx.ShouldBindQuery(&req); err != nil { 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 } pkg, err := s.svc.PackageSvc().Get(req.PackageID) if err != nil { log.Warnf("getting package: %s", err.Error()) - ctx.JSON(http.StatusOK, FailedError(err)) + ctx.JSON(http.StatusOK, types.FailedError(err)) 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) { @@ -52,18 +53,18 @@ func (s *PackageService) GetByFullName(ctx *gin.Context) { var req cliapi.PackageGetByFullName if err := ctx.ShouldBindQuery(&req); err != nil { 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 } pkg, err := s.svc.PackageSvc().GetByFullName(req.BucketName, req.PackageName) if err != nil { log.Warnf("getting package by name: %s", err.Error()) - ctx.JSON(http.StatusOK, FailedError(err)) + ctx.JSON(http.StatusOK, types.FailedError(err)) return } - ctx.JSON(http.StatusOK, OK(cliapi.PackageGetByFullNameResp{Package: pkg})) + ctx.JSON(http.StatusOK, types.OK(cliapi.PackageGetByFullNameResp{Package: pkg})) } // Create 处理创建新包的HTTP请求。 @@ -72,18 +73,18 @@ func (s *PackageService) Create(ctx *gin.Context) { var req cliapi.PackageCreate if err := ctx.ShouldBindJSON(&req); err != nil { 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 } pkg, err := s.svc.PackageSvc().Create(req.BucketID, req.Name) if err != nil { log.Warnf("creating package: %s", err.Error()) - ctx.JSON(http.StatusOK, FailedError(err)) + ctx.JSON(http.StatusOK, types.FailedError(err)) return } - ctx.JSON(http.StatusOK, OK(cliapi.PackageCreateResp{ + ctx.JSON(http.StatusOK, types.OK(cliapi.PackageCreateResp{ Package: pkg, })) } @@ -99,20 +100,20 @@ func (s *PackageService) CreateLoad(ctx *gin.Context) { var req PackageCreateUpload if err := ctx.ShouldBind(&req); err != nil { 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 } if len(req.Info.CopyTo) != len(req.Info.CopyToPath) { 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 } up, err := s.svc.Uploader.BeginCreateUpload(req.Info.BucketID, req.Info.Name, req.Info.CopyTo, req.Info.CopyToPath) if err != nil { 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 } defer up.Abort() @@ -122,14 +123,14 @@ func (s *PackageService) CreateLoad(ctx *gin.Context) { f, err := file.Open() if err != nil { 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 } path, err := url.PathUnescape(file.Filename) if err != nil { 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 } path = filepath.ToSlash(path) @@ -137,7 +138,7 @@ func (s *PackageService) CreateLoad(ctx *gin.Context) { err = up.Upload(path, f) if err != nil { 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 } pathes = append(pathes, path) @@ -146,7 +147,7 @@ func (s *PackageService) CreateLoad(ctx *gin.Context) { ret, err := up.Commit() if err != nil { 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 } @@ -155,7 +156,7 @@ func (s *PackageService) CreateLoad(ctx *gin.Context) { 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) { @@ -164,18 +165,18 @@ func (s *PackageService) Delete(ctx *gin.Context) { var req cliapi.PackageDelete if err := ctx.ShouldBindJSON(&req); err != nil { 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 } err := s.svc.PackageSvc().DeletePackage(req.PackageID) if err != nil { 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 } - ctx.JSON(http.StatusOK, OK(nil)) + ctx.JSON(http.StatusOK, types.OK(nil)) } func (s *PackageService) Clone(ctx *gin.Context) { @@ -184,18 +185,18 @@ func (s *PackageService) Clone(ctx *gin.Context) { var req cliapi.PackageClone if err := ctx.ShouldBindJSON(&req); err != nil { 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 } pkg, err := s.svc.PackageSvc().Clone(req.PackageID, req.BucketID, req.Name) if err != nil { 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 } - ctx.JSON(http.StatusOK, OK(cliapi.PackageCloneResp{ + ctx.JSON(http.StatusOK, types.OK(cliapi.PackageCloneResp{ Package: pkg, })) } @@ -206,18 +207,18 @@ func (s *PackageService) ListBucketPackages(ctx *gin.Context) { var req cliapi.PackageListBucketPackages if err := ctx.ShouldBindQuery(&req); err != nil { 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 } pkgs, err := s.svc.PackageSvc().GetBucketPackages(req.BucketID) if err != nil { 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 } - ctx.JSON(http.StatusOK, OK(cliapi.PackageListBucketPackagesResp{ + ctx.JSON(http.StatusOK, types.OK(cliapi.PackageListBucketPackagesResp{ Packages: pkgs, })) } diff --git a/client/internal/http/v1/presigned.go b/client/internal/http/v1/presigned.go index c4d2d7c..36a94d1 100644 --- a/client/internal/http/v1/presigned.go +++ b/client/internal/http/v1/presigned.go @@ -9,11 +9,12 @@ import ( "path/filepath" "github.com/gin-gonic/gin" - "gitlink.org.cn/cloudream/common/consts/errorcode" "gitlink.org.cn/cloudream/common/pkgs/logger" "gitlink.org.cn/cloudream/common/utils/math2" "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" + "gitlink.org.cn/cloudream/jcs-pub/common/ecode" ) type PresignedService struct { @@ -32,18 +33,18 @@ func (s *PresignedService) ObjectListByPath(ctx *gin.Context) { var req cliapi.PresignedObjectListByPath if err := ctx.ShouldBindQuery(&req); err != nil { 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 } resp, err := s.svc.ObjectSvc().GetByPath(req.ObjectListByPath) if err != nil { 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 } - ctx.JSON(http.StatusOK, OK(resp)) + ctx.JSON(http.StatusOK, types.OK(resp)) } func (s *PresignedService) ObjectDownloadByPath(ctx *gin.Context) { @@ -52,7 +53,7 @@ func (s *PresignedService) ObjectDownloadByPath(ctx *gin.Context) { var req cliapi.PresignedObjectDownloadByPath if err := ctx.ShouldBindQuery(&req); err != nil { 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 } @@ -61,12 +62,12 @@ func (s *PresignedService) ObjectDownloadByPath(ctx *gin.Context) { }) if err != nil { 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 } if len(resp.Objects) == 0 { 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 } @@ -83,7 +84,7 @@ func (s *PresignedService) ObjectDownloadByPath(ctx *gin.Context) { }) if err != nil { 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 } defer file.File.Close() @@ -108,7 +109,7 @@ func (s *PresignedService) ObjectDownload(ctx *gin.Context) { var req cliapi.PresignedObjectDownload if err := ctx.ShouldBindQuery(&req); err != nil { 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 } @@ -125,7 +126,7 @@ func (s *PresignedService) ObjectDownload(ctx *gin.Context) { }) if err != nil { 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 } defer file.File.Close() @@ -150,14 +151,14 @@ func (s *PresignedService) ObjectUpload(ctx *gin.Context) { var req cliapi.PresignedObjectUpload if err := ctx.ShouldBindQuery(&req); err != nil { 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 } up, err := s.svc.Uploader.BeginUpdate(req.PackageID, req.Affinity, req.CopyTo, req.CopyToPath) if err != nil { 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 } defer up.Abort() @@ -167,18 +168,18 @@ func (s *PresignedService) ObjectUpload(ctx *gin.Context) { err = up.Upload(path, ctx.Request.Body) if err != nil { 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 } ret, err := up.Commit() if err != nil { 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 } - 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) { @@ -187,18 +188,18 @@ func (s *PresignedService) ObjectNewMultipartUpload(ctx *gin.Context) { var req cliapi.PresignedObjectNewMultipartUpload if err := ctx.ShouldBindQuery(&req); err != nil { 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 } obj, err := s.svc.ObjectSvc().NewMultipartUploadObject(req.PackageID, req.Path) if err != nil { 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 } - 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) { @@ -207,18 +208,18 @@ func (s *PresignedService) ObjectUploadPart(ctx *gin.Context) { var req cliapi.PresignedObjectUploadPart if err := ctx.ShouldBindQuery(&req); err != nil { 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 } err := s.svc.Uploader.UploadPart(req.ObjectID, req.Index, ctx.Request.Body) if err != nil { 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 } - ctx.JSON(http.StatusOK, OK(cliapi.ObjectUploadPartResp{})) + ctx.JSON(http.StatusOK, types.OK(cliapi.ObjectUploadPartResp{})) } func (s *PresignedService) ObjectCompleteMultipartUpload(ctx *gin.Context) { @@ -227,16 +228,16 @@ func (s *PresignedService) ObjectCompleteMultipartUpload(ctx *gin.Context) { var req cliapi.PresignedObjectCompleteMultipartUpload if err := ctx.ShouldBindQuery(&req); err != nil { 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 } obj, err := s.svc.ObjectSvc().CompleteMultipartUpload(req.ObjectID, req.Indexes) if err != nil { 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 } - ctx.JSON(http.StatusOK, OK(cliapi.ObjectCompleteMultipartUploadResp{Object: obj})) + ctx.JSON(http.StatusOK, types.OK(cliapi.ObjectCompleteMultipartUploadResp{Object: obj})) } diff --git a/client/internal/http/v1/server.go b/client/internal/http/v1/server.go index 488aafb..1e141ba 100644 --- a/client/internal/http/v1/server.go +++ b/client/internal/http/v1/server.go @@ -2,6 +2,7 @@ package http import ( "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/services" 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) } diff --git a/client/internal/http/v1/user_space.go b/client/internal/http/v1/user_space.go index 96333dd..5351dc2 100644 --- a/client/internal/http/v1/user_space.go +++ b/client/internal/http/v1/user_space.go @@ -5,9 +5,10 @@ import ( "net/http" "github.com/gin-gonic/gin" - "gitlink.org.cn/cloudream/common/consts/errorcode" "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" + "gitlink.org.cn/cloudream/jcs-pub/common/ecode" ) type UserSpaceService struct { @@ -26,18 +27,18 @@ func (s *UserSpaceService) DownloadPackage(ctx *gin.Context) { var req cliapi.UserSpaceDownloadPackageReq if err := ctx.ShouldBindJSON(&req); err != nil { 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 } err := s.svc.UserSpaceSvc().DownloadPackage(req.PackageID, req.UserSpaceID, req.RootPath) if err != nil { 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 } - ctx.JSON(http.StatusOK, OK(cliapi.UserSpaceDownloadPackageResp{})) + ctx.JSON(http.StatusOK, types.OK(cliapi.UserSpaceDownloadPackageResp{})) } func (s *UserSpaceService) CreatePackage(ctx *gin.Context) { @@ -46,18 +47,18 @@ func (s *UserSpaceService) CreatePackage(ctx *gin.Context) { var req cliapi.UserSpaceCreatePackageReq if err := ctx.ShouldBindJSON(&req); err != nil { 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 } pkg, err := s.svc.Uploader.UserSpaceUpload(req.UserSpaceID, req.Path, req.BucketID, req.Name, req.SpaceAffinity) if err != nil { 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 } - ctx.JSON(http.StatusOK, OK(cliapi.UserSpaceCreatePackageResp{ + ctx.JSON(http.StatusOK, types.OK(cliapi.UserSpaceCreatePackageResp{ Package: *pkg, })) } @@ -68,18 +69,18 @@ func (s *UserSpaceService) Get(ctx *gin.Context) { var req cliapi.UserSpaceGet if err := ctx.ShouldBindQuery(&req); err != nil { 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 } info, err := s.svc.UserSpaceSvc().Get(req.UserSpaceID) if err != nil { 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 } - ctx.JSON(http.StatusOK, OK(cliapi.UserSpaceGetResp{ + ctx.JSON(http.StatusOK, types.OK(cliapi.UserSpaceGetResp{ UserSpace: info, })) } @@ -87,42 +88,42 @@ func (s *UserSpaceService) Get(ctx *gin.Context) { func (s *UserSpaceService) Create(ctx *gin.Context) { log := logger.WithField("HTTP", "UserSpace.Create") - req, err := ShouldBindJSONEx[cliapi.UserSpaceCreate](ctx) + req, err := types.ShouldBindJSONEx[cliapi.UserSpaceCreate](ctx) if err != nil { 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 } resp, cerr := s.svc.UserSpaceSvc().Create(req) if cerr != nil { 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 } - ctx.JSON(http.StatusOK, OK(resp)) + ctx.JSON(http.StatusOK, types.OK(resp)) } func (s *UserSpaceService) Update(ctx *gin.Context) { log := logger.WithField("HTTP", "UserSpace.Update") var req cliapi.UserSpaceUpdate - req, err := ShouldBindJSONEx[cliapi.UserSpaceUpdate](ctx) + req, err := types.ShouldBindJSONEx[cliapi.UserSpaceUpdate](ctx) if err != nil { 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 } resp, cerr := s.svc.UserSpaceSvc().Update(req) if cerr != nil { 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 } - ctx.JSON(http.StatusOK, OK(resp)) + ctx.JSON(http.StatusOK, types.OK(resp)) } func (s *UserSpaceService) Delete(ctx *gin.Context) { @@ -131,39 +132,39 @@ func (s *UserSpaceService) Delete(ctx *gin.Context) { var req cliapi.UserSpaceDelete if err := ctx.ShouldBindJSON(&req); err != nil { 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 } resp, cerr := s.svc.UserSpaceSvc().Delete(req) if cerr != nil { 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 } - ctx.JSON(http.StatusOK, OK(resp)) + ctx.JSON(http.StatusOK, types.OK(resp)) } func (s *UserSpaceService) Test(ctx *gin.Context) { log := logger.WithField("HTTP", "UserSpace.Test") var req cliapi.UserSpaceTest - req, err := ShouldBindJSONEx[cliapi.UserSpaceTest](ctx) + req, err := types.ShouldBindJSONEx[cliapi.UserSpaceTest](ctx) if err != nil { 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 } resp, cerr := s.svc.UserSpaceSvc().Test(req) if cerr != nil { 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 } - ctx.JSON(http.StatusOK, OK(resp)) + ctx.JSON(http.StatusOK, types.OK(resp)) } func (s *UserSpaceService) SpaceToSpace(ctx *gin.Context) { @@ -172,18 +173,18 @@ func (s *UserSpaceService) SpaceToSpace(ctx *gin.Context) { var req cliapi.UserSpaceSpaceToSpace if err := ctx.ShouldBindJSON(&req); err != nil { 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 } ret, err := s.svc.UserSpaceSvc().SpaceToSpace(req.SrcUserSpaceID, req.SrcPath, req.DstUserSpaceID, req.DstPath) if err != nil { 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 } - ctx.JSON(http.StatusOK, OK(cliapi.UserSpaceSpaceToSpaceResp{ + ctx.JSON(http.StatusOK, types.OK(cliapi.UserSpaceSpaceToSpaceResp{ SpaceToSpaceResult: ret, })) } diff --git a/client/sdk/signer/signer.go b/client/sdk/signer/signer.go new file mode 100644 index 0000000..78bb955 --- /dev/null +++ b/client/sdk/signer/signer.go @@ -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() +} diff --git a/common/pkgs/storage/obs/obs.go b/common/pkgs/storage/obs/obs.go index 5c20933..f0641fc 100644 --- a/common/pkgs/storage/obs/obs.go +++ b/common/pkgs/storage/obs/obs.go @@ -79,6 +79,7 @@ func createClient(stgType *cortypes.OBSType, cred *cortypes.OBSCred) (*s3.Client } awsConfig.Credentials = &credentials.StaticCredentialsProvider{Value: cre} awsConfig.Region = stgType.Region + awsConfig.RetryMaxAttempts = 1 options := []func(*s3.Options){} options = append(options, func(s3Opt *s3.Options) { diff --git a/coordinator/internal/cmd/cert.go b/coordinator/internal/cmd/cert.go index 24afb98..fa63042 100644 --- a/coordinator/internal/cmd/cert.go +++ b/coordinator/internal/cmd/cert.go @@ -1,8 +1,9 @@ package cmd import ( + "crypto/ecdsa" + "crypto/elliptic" "crypto/rand" - "crypto/rsa" "crypto/x509" "crypto/x509/pkix" "encoding/pem" @@ -58,7 +59,7 @@ func init() { } func certRoot(output string) { - caPriv, _ := rsa.GenerateKey(rand.Reader, 2048) + caPriv, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) // 创建 CA 证书模板 caTemplate := &x509.Certificate{ @@ -78,7 +79,9 @@ func certRoot(output string) { // 保存 CA 证书和私钥 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) } @@ -111,14 +114,14 @@ func certServer(certFile string, keyFile string, output string) { return } - caKey, err := x509.ParsePKCS1PrivateKey(caKeyPEMBlock.Bytes) + caKey, err := x509.ParseECPrivateKey(caKeyPEMBlock.Bytes) if err != nil { fmt.Println("Failed to parse CA key:", err) return } // 生成服务端私钥 - serverPriv, _ := rsa.GenerateKey(rand.Reader, 2048) + serverPriv, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) // 服务端证书模板 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_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) } @@ -174,14 +179,14 @@ func certClient(certFile string, keyFile string, output string) { return } - caKey, err := x509.ParsePKCS1PrivateKey(caKeyPEMBlock.Bytes) + caKey, err := x509.ParseECPrivateKey(caKeyPEMBlock.Bytes) if err != nil { fmt.Println("Failed to parse CA key:", err) return } // 生成客户端私钥 - clientPriv, _ := rsa.GenerateKey(rand.Reader, 2048) + clientPriv, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) // 客户端证书模板 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_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) }