From ac530e506b188107541d59ded14ecfa812213414 Mon Sep 17 00:00:00 2001 From: Bruce Date: Wed, 11 May 2022 11:44:19 +0800 Subject: [PATCH 1/5] feat: middleware --- .../handler/gateway/disonnecthandler.go | 33 ++++ .../api/studio/internal/handler/routes.go | 5 + .../internal/logic/gateway/disonnectlogic.go | 29 ++++ .../api/studio/internal/service/gateway.go | 49 +----- server-v2/api/studio/internal/types/types.go | 5 + server-v2/api/studio/pkg/auth/authorize.go | 118 ++++++++----- server-v2/api/studio/pkg/middleware/assets.go | 100 +++++++++++ server-v2/api/studio/pkg/utils/http.go | 157 +++++------------- server-v2/api/studio/pkg/utils/route.go | 58 ------- server-v2/api/studio/restapi/gateway.api | 8 + server-v2/api/studio/studio.go | 13 +- 11 files changed, 314 insertions(+), 261 deletions(-) create mode 100644 server-v2/api/studio/internal/handler/gateway/disonnecthandler.go create mode 100644 server-v2/api/studio/internal/logic/gateway/disonnectlogic.go create mode 100644 server-v2/api/studio/pkg/middleware/assets.go delete mode 100644 server-v2/api/studio/pkg/utils/route.go diff --git a/server-v2/api/studio/internal/handler/gateway/disonnecthandler.go b/server-v2/api/studio/internal/handler/gateway/disonnecthandler.go new file mode 100644 index 00000000..ef7418dd --- /dev/null +++ b/server-v2/api/studio/internal/handler/gateway/disonnecthandler.go @@ -0,0 +1,33 @@ +// Code generated by goctl. DO NOT EDIT. +package gateway + +import ( + "net/http" + + "github.com/vesoft-inc/go-pkg/validator" + "github.com/vesoft-inc/nebula-studio/server/api/studio/pkg/ecode" + + "github.com/vesoft-inc/nebula-studio/server/api/studio/internal/logic/gateway" + "github.com/vesoft-inc/nebula-studio/server/api/studio/internal/svc" + "github.com/vesoft-inc/nebula-studio/server/api/studio/internal/types" + "github.com/zeromicro/go-zero/rest/httpx" +) + +func DisonnectHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req types.DisconnectDBParams + if err := httpx.Parse(r, &req); err != nil { + err = ecode.WithCode(ecode.ErrParam, err) + svcCtx.ResponseHandler.Handle(w, r, nil, err) + return + } + if err := validator.Struct(req); err != nil { + svcCtx.ResponseHandler.Handle(w, r, nil, err) + return + } + + l := gateway.NewDisonnectLogic(r.Context(), svcCtx) + data, err := l.Disonnect(req) + svcCtx.ResponseHandler.Handle(w, r, data, err) + } +} diff --git a/server-v2/api/studio/internal/handler/routes.go b/server-v2/api/studio/internal/handler/routes.go index f47653c5..3ac7849a 100644 --- a/server-v2/api/studio/internal/handler/routes.go +++ b/server-v2/api/studio/internal/handler/routes.go @@ -41,6 +41,11 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { Path: "/connect", Handler: gateway.ConnectHandler(serverCtx), }, + { + Method: http.MethodPost, + Path: "/disconnect", + Handler: gateway.DisonnectHandler(serverCtx), + }, }, rest.WithPrefix("/api-nebula/db"), ) diff --git a/server-v2/api/studio/internal/logic/gateway/disonnectlogic.go b/server-v2/api/studio/internal/logic/gateway/disonnectlogic.go new file mode 100644 index 00000000..a49cd1aa --- /dev/null +++ b/server-v2/api/studio/internal/logic/gateway/disonnectlogic.go @@ -0,0 +1,29 @@ +package gateway + +import ( + "context" + + "github.com/vesoft-inc/nebula-studio/server/api/studio/internal/service" + "github.com/vesoft-inc/nebula-studio/server/api/studio/internal/svc" + "github.com/vesoft-inc/nebula-studio/server/api/studio/internal/types" + + "github.com/zeromicro/go-zero/core/logx" +) + +type DisonnectLogic struct { + logx.Logger + ctx context.Context + svcCtx *svc.ServiceContext +} + +func NewDisonnectLogic(ctx context.Context, svcCtx *svc.ServiceContext) DisonnectLogic { + return DisonnectLogic{ + Logger: logx.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + } +} + +func (l *DisonnectLogic) Disonnect(req types.DisconnectDBParams) (*types.AnyResponse, error) { + return service.NewGatewayService(l.ctx, l.svcCtx).DisconnectDB(&req) +} diff --git a/server-v2/api/studio/internal/service/gateway.go b/server-v2/api/studio/internal/service/gateway.go index 87ee65cb..ca7be670 100644 --- a/server-v2/api/studio/internal/service/gateway.go +++ b/server-v2/api/studio/internal/service/gateway.go @@ -2,15 +2,10 @@ package service import ( "context" - "encoding/base64" - "fmt" - "strings" "github.com/vesoft-inc/nebula-http-gateway/ccore/nebula/gateway/dao" "github.com/vesoft-inc/nebula-studio/server/api/studio/internal/svc" "github.com/vesoft-inc/nebula-studio/server/api/studio/internal/types" - "github.com/vesoft-inc/nebula-studio/server/api/studio/pkg/auth" - "github.com/vesoft-inc/nebula-studio/server/api/studio/pkg/ecode" "github.com/zeromicro/go-zero/core/logx" ) @@ -21,6 +16,7 @@ type ( GatewayService interface { GetExec(request *types.ExecNGQLParams) (*types.AnyResponse, error) ConnectDB(request *types.ConnectDBParams) (*types.ConnectDBResult, error) + DisconnectDB(request *types.DisconnectDBParams) (*types.AnyResponse, error) } gatewayService struct { @@ -47,41 +43,14 @@ func (s *gatewayService) GetExec(request *types.ExecNGQLParams) (*types.AnyRespo } func (s *gatewayService) ConnectDB(request *types.ConnectDBParams) (*types.ConnectDBResult, error) { - fmt.Println("======request999", request) - tokenSplit := strings.Split(request.Authorization, " ") - if len(tokenSplit) != 2 { - return nil, ecode.WithCode(ecode.ErrParam, nil, "invalid authorization") - } - - decode, err := base64.StdEncoding.DecodeString(tokenSplit[1]) - if err != nil { - return nil, ecode.WithCode(ecode.ErrParam, err) - } - - loginInfo := strings.Split(string(decode), ":") - if len(loginInfo) < 2 { - return nil, ecode.WithCode(ecode.ErrParam, nil, "len of account is less than two") - } - - username, password := loginInfo[0], loginInfo[1] - clientInfo, err := dao.Connect(request.Address, request.Port, username, password) - - tokenString, err := auth.CreateToken( - &auth.AuthData{ - NebulaAddress: request.Address, - Username: username, - ClientID: clientInfo.ClientID, - }, - &s.svcCtx.Config, - ) - - fmt.Println("=====tokenString", tokenString) - - if err != nil { - return nil, ecode.WithInternalServer(err, "connect db failed") - } - return &types.ConnectDBResult{ - Version: string(clientInfo.NebulaVersion), + Version: string(request.NebulaVersion), }, nil } + +func (s *gatewayService) DisconnectDB(request *types.DisconnectDBParams) (*types.AnyResponse, error) { + if request.Nsid != "" { + dao.Disconnect(request.Nsid) + } + return nil, nil +} diff --git a/server-v2/api/studio/internal/types/types.go b/server-v2/api/studio/internal/types/types.go index 373f0ca5..e5d2d7fb 100644 --- a/server-v2/api/studio/internal/types/types.go +++ b/server-v2/api/studio/internal/types/types.go @@ -13,6 +13,7 @@ type ExecNGQLParams struct { type ConnectDBParams struct { Address string `json:"address"` Port int `json:"port"` + NebulaVersion string `form:"nebulaVersion,optional"` Authorization string `header:"Authorization"` } @@ -20,6 +21,10 @@ type ConnectDBResult struct { Version string `json:"version"` } +type DisconnectDBParams struct { + Nsid string `form:"nsid,optional"` +} + type AnyResponse struct { Data interface{} `json:"data"` } diff --git a/server-v2/api/studio/pkg/auth/authorize.go b/server-v2/api/studio/pkg/auth/authorize.go index b75c27f9..6513a4df 100644 --- a/server-v2/api/studio/pkg/auth/authorize.go +++ b/server-v2/api/studio/pkg/auth/authorize.go @@ -9,8 +9,11 @@ import ( "github.com/golang-jwt/jwt/v4" "github.com/vesoft-inc/nebula-http-gateway/ccore/nebula/gateway/dao" + "github.com/vesoft-inc/nebula-http-gateway/ccore/nebula/gateway/pool" "github.com/vesoft-inc/nebula-studio/server/api/studio/internal/config" "github.com/vesoft-inc/nebula-studio/server/api/studio/internal/types" + "github.com/vesoft-inc/nebula-studio/server/api/studio/pkg/ecode" + "github.com/vesoft-inc/nebula-studio/server/api/studio/pkg/utils" "github.com/zeromicro/go-zero/rest" "github.com/zeromicro/go-zero/rest/httpx" ) @@ -32,6 +35,38 @@ type ( var globalConfig = new(config.Config) +func parseConnectDBParams(params *types.ConnectDBParams, config *config.Config) (string, *pool.ClientInfo, error) { + tokenSplit := strings.Split(params.Authorization, " ") + if len(tokenSplit) != 2 { + return "", nil, ecode.WithCode(ecode.ErrParam, nil, "invalid authorization") + } + + decode, err := base64.StdEncoding.DecodeString(tokenSplit[1]) + if err != nil { + return "", nil, ecode.WithCode(ecode.ErrParam, err) + } + + loginInfo := strings.Split(string(decode), ":") + if len(loginInfo) < 2 { + return "", nil, ecode.WithCode(ecode.ErrParam, nil, "len of account is less than two") + } + + username, password := loginInfo[0], loginInfo[1] + clientInfo, err := dao.Connect(params.Address, params.Port, username, password) + if err != nil { + return "", nil, ecode.WithCode(ecode.ErrInternalServer, err) + } + + tokenString, err := CreateToken( + &AuthData{ + NebulaAddress: params.Address, + Username: username, + }, + config, + ) + return tokenString, clientInfo, err +} + func CreateToken(authData *AuthData, config *config.Config) (string, error) { now := time.Now() expiresAt := now.Add(time.Duration(config.Auth.AccessExpire) * time.Second).Unix() @@ -52,52 +87,57 @@ func AuthMiddlewareWithConfig(config *config.Config) rest.Middleware { return func(w http.ResponseWriter, r *http.Request) { // login handler if strings.HasSuffix(r.URL.Path, "/connect") { - fmt.Println("=====global middleware", r.URL.Path) var req types.ConnectDBParams - err := httpx.Parse(r, &req) + rClone := utils.CopyHttpRequest(r) + err := httpx.Parse(rClone, &req) if err != nil { - fmt.Println("=====req3333", req) + http.Error(w, err.Error(), http.StatusBadRequest) + return } - fmt.Println("=====err", err) - fmt.Println("=====req.Address", req.Address) - fmt.Println("=====req.Port", req.Port) - fmt.Println("=====req.Authorization", req.Authorization) - } - c1 := http.Cookie{ - Name: "access_token", - Value: "12333", - Path: "/", - HttpOnly: true, - MaxAge: 3600, - } - var req1 types.ConnectDBParams - err1 := httpx.Parse(r, &req1) - fmt.Println("=====err1=====", err1) + tokenString, clientInfo, err := parseConnectDBParams(&req, config) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } - w.Header().Set("Access-Control-Allow-Origin", "*") - w.Header().Set("Set-Cookie", c1.String()) - next(w, r) - } - } -} + token := http.Cookie{ + Name: "token", + Value: tokenString, + Path: "/", + HttpOnly: true, + MaxAge: 1800, + } + nsid := http.Cookie{ + Name: "nsid", + Value: clientInfo.ClientID, + Path: "/", + HttpOnly: true, + MaxAge: 1800, + } -func AuthMiddleware(next http.HandlerFunc) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - // login handler - if strings.HasSuffix(r.URL.Path, "/connect") { - fmt.Println("=====global middleware", r.URL.Path) - } - c1 := http.Cookie{ - Name: "access_token", - Value: "12333", - Path: "/", - HttpOnly: true, - MaxAge: 3600, + query := r.URL.Query() + query.Set("nebulaVersion", string(clientInfo.NebulaVersion)) + r.URL, _ = r.URL.Parse(r.URL.Path + "?" + query.Encode()) + + // w.Header().Set("Access-Control-Allow-Origin", "*") + w.Header().Set("Set-Cookie", token.String()) + w.Header().Add("Set-Cookie", nsid.String()) + } else if strings.HasSuffix(r.URL.Path, "/disconnect") { + nsidCookie, err := r.Cookie("nsid") + + if err == nil { + query := r.URL.Query() + query.Set("nsid", nsidCookie.Value) + r.URL, _ = r.URL.Parse(r.URL.Path + "?" + query.Encode()) + } + + w.Header().Set("Set-Cookie", utils.DisabledCookie("token").String()) + w.Header().Add("Set-Cookie", utils.DisabledCookie("nsid").String()) + } else { + } + next(w, r) } - w.Header().Set("Access-Control-Allow-Origin", "*") - w.Header().Set("Set-Cookie", c1.String()) - next(w, r) } } diff --git a/server-v2/api/studio/pkg/middleware/assets.go b/server-v2/api/studio/pkg/middleware/assets.go new file mode 100644 index 00000000..a58a2abb --- /dev/null +++ b/server-v2/api/studio/pkg/middleware/assets.go @@ -0,0 +1,100 @@ +package middleware + +import ( + "net/http" + "net/url" + "path/filepath" + "strings" +) + +type ( + AssertsConfig struct { + Prefix string + Root string + Index string + SPA bool + Filesystem http.FileSystem + } + + assertsHandler struct { + config AssertsConfig + } +) + +func NewAssertsHandler(config AssertsConfig) http.Handler { + if config.Root == "" { + config.Root = "." + } + if config.Index == "" { + config.Index = "/index.html" + } else { + config.Index = filepath.Clean("/" + config.Index) + } + if config.Filesystem == nil { + config.Filesystem = http.Dir(config.Root) + config.Root = "." + } + + return &assertsHandler{ + config: config, + } +} + +func (h *assertsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + urlPath, err := url.PathUnescape(r.URL.Path) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + pathInFs := filepath.Clean("/" + urlPath) + if h.config.Prefix != "" { + if strings.HasPrefix(urlPath, h.config.Prefix) { + pathInFs = filepath.Clean("/" + urlPath[len(h.config.Prefix):]) + } else { + if !h.config.SPA { + http.NotFound(w, r) + return + } + pathInFs = h.config.Index + } + } + + file, err := h.config.Filesystem.Open(filepath.Join(h.config.Root, pathInFs)) + if err != nil { + if !h.config.SPA || urlPath == h.config.Index { + http.NotFound(w, r) + return + } + pathInFs = h.config.Index + file, err = h.config.Filesystem.Open(filepath.Join(h.config.Root, pathInFs)) + if err != nil { + http.NotFound(w, r) + return + } + } + defer file.Close() + + stat, err := file.Stat() + if err != nil { + http.NotFound(w, r) + return + } + + if stat.IsDir() { + pathInFs = filepath.Join(pathInFs, h.config.Index) + file, err = h.config.Filesystem.Open(filepath.Join(h.config.Root, pathInFs)) + if err != nil { + http.NotFound(w, r) + return + } + defer file.Close() + stat, err = file.Stat() + if err != nil || stat.IsDir() { + http.NotFound(w, r) + return + } + } + + http.ServeContent(w, r, stat.Name(), stat.ModTime(), file) +} diff --git a/server-v2/api/studio/pkg/utils/http.go b/server-v2/api/studio/pkg/utils/http.go index f81d283f..a3ae820d 100644 --- a/server-v2/api/studio/pkg/utils/http.go +++ b/server-v2/api/studio/pkg/utils/http.go @@ -1,128 +1,47 @@ package utils +import ( + "bytes" + "io" + "io/ioutil" + "net/http" +) + const MIMEOctetStream = "application/octet-stream" -// GetMIME returns the content-type of a file extension -func GetMIME(extension string) (mime string) { - if len(extension) == 0 { - return mime - } - if extension[0] == '.' { - mime = mimeExtensions[extension[1:]] - } else { - mime = mimeExtensions[extension] +func CopyHttpRequest(r *http.Request) *http.Request { + reqCopy := new(http.Request) + + if r == nil { + return reqCopy } - if len(mime) == 0 { - return MIMEOctetStream + + *reqCopy = *r + + if r.Body != nil { + defer r.Body.Close() + + // Buffer body data + var bodyBuffer bytes.Buffer + newBodyBuffer := new(bytes.Buffer) + + io.Copy(&bodyBuffer, r.Body) + *newBodyBuffer = bodyBuffer + + // Create new ReadClosers so we can split output + r.Body = ioutil.NopCloser(&bodyBuffer) + reqCopy.Body = ioutil.NopCloser(newBodyBuffer) } - return mime + + return reqCopy } -// MIME types were copied from https://github.com/nginx/nginx/blob/master/conf/mime.types -var mimeExtensions = map[string]string{ - "html": "text/html", - "htm": "text/html", - "shtml": "text/html", - "css": "text/css", - "gif": "image/gif", - "jpeg": "image/jpeg", - "jpg": "image/jpeg", - "xml": "application/xml", - "js": "application/javascript", - "atom": "application/atom+xml", - "rss": "application/rss+xml", - "mml": "text/mathml", - "txt": "text/plain", - "jad": "text/vnd.sun.j2me.app-descriptor", - "wml": "text/vnd.wap.wml", - "htc": "text/x-component", - "png": "image/png", - "svg": "image/svg+xml", - "svgz": "image/svg+xml", - "tif": "image/tiff", - "tiff": "image/tiff", - "wbmp": "image/vnd.wap.wbmp", - "webp": "image/webp", - "ico": "image/x-icon", - "jng": "image/x-jng", - "bmp": "image/x-ms-bmp", - "woff": "font/woff", - "woff2": "font/woff2", - "jar": "application/java-archive", - "war": "application/java-archive", - "ear": "application/java-archive", - "json": "application/json", - "hqx": "application/mac-binhex40", - "doc": "application/msword", - "pdf": "application/pdf", - "ps": "application/postscript", - "eps": "application/postscript", - "ai": "application/postscript", - "rtf": "application/rtf", - "m3u8": "application/vnd.apple.mpegurl", - "kml": "application/vnd.google-earth.kml+xml", - "kmz": "application/vnd.google-earth.kmz", - "xls": "application/vnd.ms-excel", - "eot": "application/vnd.ms-fontobject", - "ppt": "application/vnd.ms-powerpoint", - "odg": "application/vnd.oasis.opendocument.graphics", - "odp": "application/vnd.oasis.opendocument.presentation", - "ods": "application/vnd.oasis.opendocument.spreadsheet", - "odt": "application/vnd.oasis.opendocument.text", - "wmlc": "application/vnd.wap.wmlc", - "7z": "application/x-7z-compressed", - "cco": "application/x-cocoa", - "jardiff": "application/x-java-archive-diff", - "jnlp": "application/x-java-jnlp-file", - "run": "application/x-makeself", - "pl": "application/x-perl", - "pm": "application/x-perl", - "prc": "application/x-pilot", - "pdb": "application/x-pilot", - "rar": "application/x-rar-compressed", - "rpm": "application/x-redhat-package-manager", - "sea": "application/x-sea", - "swf": "application/x-shockwave-flash", - "sit": "application/x-stuffit", - "tcl": "application/x-tcl", - "tk": "application/x-tcl", - "der": "application/x-x509-ca-cert", - "pem": "application/x-x509-ca-cert", - "crt": "application/x-x509-ca-cert", - "xpi": "application/x-xpinstall", - "xhtml": "application/xhtml+xml", - "xspf": "application/xspf+xml", - "zip": "application/zip", - "bin": "application/octet-stream", - "exe": "application/octet-stream", - "dll": "application/octet-stream", - "deb": "application/octet-stream", - "dmg": "application/octet-stream", - "iso": "application/octet-stream", - "img": "application/octet-stream", - "msi": "application/octet-stream", - "msp": "application/octet-stream", - "msm": "application/octet-stream", - "mid": "audio/midi", - "midi": "audio/midi", - "kar": "audio/midi", - "mp3": "audio/mpeg", - "ogg": "audio/ogg", - "m4a": "audio/x-m4a", - "ra": "audio/x-realaudio", - "3gpp": "video/3gpp", - "3gp": "video/3gpp", - "ts": "video/mp2t", - "mp4": "video/mp4", - "mpeg": "video/mpeg", - "mpg": "video/mpeg", - "mov": "video/quicktime", - "webm": "video/webm", - "flv": "video/x-flv", - "m4v": "video/x-m4v", - "mng": "video/x-mng", - "asx": "video/x-ms-asf", - "asf": "video/x-ms-asf", - "wmv": "video/x-ms-wmv", - "avi": "video/x-msvideo", +func DisabledCookie(name string) *http.Cookie { + return &http.Cookie{ + Name: name, + Value: "", + Path: "/", + HttpOnly: true, + MaxAge: -1, + } } diff --git a/server-v2/api/studio/pkg/utils/route.go b/server-v2/api/studio/pkg/utils/route.go deleted file mode 100644 index 13ad7ef6..00000000 --- a/server-v2/api/studio/pkg/utils/route.go +++ /dev/null @@ -1,58 +0,0 @@ -package utils - -import ( - "embed" - "net/http" - "path" - "path/filepath" - "strconv" - "strings" - - "github.com/vesoft-inc/nebula-studio/server/api/studio/internal/svc" - "github.com/zeromicro/go-zero/core/logx" - "github.com/zeromicro/go-zero/rest" -) - -var embedAssetsFolder = "assets" - -func RegisterHandlers(engine *rest.Server, serverCtx *svc.ServiceContext, fs embed.FS) { - dirlevel := []string{"/:1", ":2", ":3", ":4", ":5", ":6", ":7", ":8"} - for i := 1; i < len(dirlevel); i++ { - pathname := strings.Join(dirlevel[:i], "/") - //最后生成 /asset - engine.AddRoute( - rest.Route{ - Method: http.MethodGet, - Path: pathname, - Handler: filehandler(fs), - }) - logx.Infof("register route %s", pathname) - } -} - -func filehandler(fs embed.FS) http.HandlerFunc { - return func(w http.ResponseWriter, req *http.Request) { - fileType := filepath.Ext(req.URL.Path) - // subpath default: index.html - if len(fileType) == 0 { - htmlFileData, _ := fs.ReadFile(path.Join(embedAssetsFolder, "index.html")) - w.Write(htmlFileData) - return - } - - // assets file: a/b/c.(js|css|png) - data, err := fs.ReadFile(path.Join(embedAssetsFolder, req.URL.Path)) - if err != nil { - logx.Errorf("open resource error %s", err.Error()) - http.NotFound(w, req) - return - } - - // set assets response header - w.Header().Set("Content-Type", GetMIME(fileType)+"; charset=utf-8") - w.Header().Set("Content-Length", strconv.Itoa(len(data))) - w.Header().Set("Cache-Control", "public, max-age=7200") - - w.Write(data) - } -} diff --git a/server-v2/api/studio/restapi/gateway.api b/server-v2/api/studio/restapi/gateway.api index 006dab1d..733c0836 100644 --- a/server-v2/api/studio/restapi/gateway.api +++ b/server-v2/api/studio/restapi/gateway.api @@ -8,11 +8,15 @@ type ( ConnectDBParams { Address string `json:"address"` Port int `json:"port"` + NebulaVersion string `form:"nebulaVersion,optional"` Authorization string `header:"Authorization"` } ConnectDBResult { Version string `json:"version"` } + DisconnectDBParams { + Nsid string `form:"nsid,optional"` + } AnyResponse { Data interface{} `json:"data"` @@ -39,4 +43,8 @@ service studio-api { @doc "Connect DB" @handler Connect post /connect(ConnectDBParams) returns (ConnectDBResult) + + @doc "Disonnect DB" + @handler Disonnect + post /disconnect(DisconnectDBParams) returns (AnyResponse) } \ No newline at end of file diff --git a/server-v2/api/studio/studio.go b/server-v2/api/studio/studio.go index 101e5787..c455e1d9 100644 --- a/server-v2/api/studio/studio.go +++ b/server-v2/api/studio/studio.go @@ -4,12 +4,13 @@ import ( "embed" "flag" "fmt" + "net/http" "github.com/vesoft-inc/nebula-studio/server/api/studio/internal/config" "github.com/vesoft-inc/nebula-studio/server/api/studio/internal/handler" "github.com/vesoft-inc/nebula-studio/server/api/studio/internal/svc" "github.com/vesoft-inc/nebula-studio/server/api/studio/pkg/auth" - "github.com/vesoft-inc/nebula-studio/server/api/studio/pkg/utils" + "github.com/vesoft-inc/nebula-studio/server/api/studio/pkg/middleware" "github.com/zeromicro/go-zero/core/conf" "github.com/zeromicro/go-zero/rest" @@ -27,16 +28,18 @@ func main() { conf.MustLoad(*configFile, &c, conf.UseEnv()) svcCtx := svc.NewServiceContext(c) - server := rest.MustNewServer(c.RestConf) + server := rest.MustNewServer(c.RestConf, rest.WithNotFoundHandler(middleware.NewAssertsHandler(middleware.AssertsConfig{ + Root: "assets", + Filesystem: http.FS(embedAssets), + SPA: true, + }))) + defer server.Stop() // global middleware // server.Use(auth.AuthMiddleware) server.Use(auth.AuthMiddlewareWithConfig(&c)) - // static assets handlers - utils.RegisterHandlers(server, svcCtx, embedAssets) - // api handlers handler.RegisterHandlers(server, svcCtx) From 9b4e37c8fdbe6d3446866772282d67d7b00bd687 Mon Sep 17 00:00:00 2001 From: Bruce Date: Wed, 11 May 2022 14:16:32 +0800 Subject: [PATCH 2/5] feat: dynamicly add query params func --- server-v2/api/studio/etc/studio-api.yaml | 2 +- server-v2/api/studio/pkg/auth/authorize.go | 8 ++------ server-v2/api/studio/pkg/utils/http.go | 9 +++++++++ server-v2/api/studio/studio.go | 1 - server-v2/go.mod | 2 ++ server-v2/go.sum | 4 ++++ 6 files changed, 18 insertions(+), 8 deletions(-) diff --git a/server-v2/api/studio/etc/studio-api.yaml b/server-v2/api/studio/etc/studio-api.yaml index 002e4cd9..6423c0ab 100644 --- a/server-v2/api/studio/etc/studio-api.yaml +++ b/server-v2/api/studio/etc/studio-api.yaml @@ -6,6 +6,6 @@ Debug: Enable: false Auth: AccessSecret: "login_secret" - AccessExpire: 7200 + AccessExpire: 1800 File: UploadDir: "./upload/" \ No newline at end of file diff --git a/server-v2/api/studio/pkg/auth/authorize.go b/server-v2/api/studio/pkg/auth/authorize.go index 6513a4df..21e86ce4 100644 --- a/server-v2/api/studio/pkg/auth/authorize.go +++ b/server-v2/api/studio/pkg/auth/authorize.go @@ -116,9 +116,7 @@ func AuthMiddlewareWithConfig(config *config.Config) rest.Middleware { MaxAge: 1800, } - query := r.URL.Query() - query.Set("nebulaVersion", string(clientInfo.NebulaVersion)) - r.URL, _ = r.URL.Parse(r.URL.Path + "?" + query.Encode()) + utils.AddQueryParams(r, map[string]string{"nebulaVersion": string(clientInfo.NebulaVersion)}) // w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Set("Set-Cookie", token.String()) @@ -127,9 +125,7 @@ func AuthMiddlewareWithConfig(config *config.Config) rest.Middleware { nsidCookie, err := r.Cookie("nsid") if err == nil { - query := r.URL.Query() - query.Set("nsid", nsidCookie.Value) - r.URL, _ = r.URL.Parse(r.URL.Path + "?" + query.Encode()) + utils.AddQueryParams(r, map[string]string{"nsid": nsidCookie.Value}) } w.Header().Set("Set-Cookie", utils.DisabledCookie("token").String()) diff --git a/server-v2/api/studio/pkg/utils/http.go b/server-v2/api/studio/pkg/utils/http.go index a3ae820d..0ad3b8cb 100644 --- a/server-v2/api/studio/pkg/utils/http.go +++ b/server-v2/api/studio/pkg/utils/http.go @@ -45,3 +45,12 @@ func DisabledCookie(name string) *http.Cookie { MaxAge: -1, } } + +// dynamicly add query params to the request +func AddQueryParams(r *http.Request, params map[string]string) { + query := r.URL.Query() + for k, v := range params { + query.Set(k, v) + } + r.URL, _ = r.URL.Parse(r.URL.Path + "?" + query.Encode()) +} diff --git a/server-v2/api/studio/studio.go b/server-v2/api/studio/studio.go index c455e1d9..77ad786e 100644 --- a/server-v2/api/studio/studio.go +++ b/server-v2/api/studio/studio.go @@ -37,7 +37,6 @@ func main() { defer server.Stop() // global middleware - // server.Use(auth.AuthMiddleware) server.Use(auth.AuthMiddlewareWithConfig(&c)) // api handlers diff --git a/server-v2/go.mod b/server-v2/go.mod index cf55c8de..6cde3328 100644 --- a/server-v2/go.mod +++ b/server-v2/go.mod @@ -16,6 +16,7 @@ require ( ) require ( + github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394 github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/go-logr/logr v1.2.2 // indirect @@ -36,6 +37,7 @@ require ( github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.30.0 // indirect github.com/prometheus/procfs v0.7.3 // indirect + github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca github.com/spaolacci/murmur3 v1.1.0 // indirect go.opentelemetry.io/otel v1.3.0 // indirect go.opentelemetry.io/otel/exporters/jaeger v1.3.0 // indirect diff --git a/server-v2/go.sum b/server-v2/go.sum index 94fe5128..4abb9dd0 100644 --- a/server-v2/go.sum +++ b/server-v2/go.sum @@ -58,6 +58,8 @@ github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGn github.com/alicebob/miniredis/v2 v2.17.0/go.mod h1:gquAfGbzn92jvtrSC69+6zZnwSODVXVpYDRaGhWaL6I= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394 h1:OYA+5W64v3OgClL+IrOD63t4i/RW7RqrAVl9LTZ9UqQ= +github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394/go.mod h1:Q8n74mJTIgjX4RBBcHnJ05h//6/k6foqmgE45jTQtxg= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -360,6 +362,8 @@ github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTE github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca h1:NugYot0LIVPxTvN8n+Kvkn6TrbMyxQiuvKdEwFdR9vI= +github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU= github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b h1:gQZ0qzfKHQIybLANtM3mBXNUtOfsCFXeTsnBqCsx1KM= github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= From 4b6e92bd9b6d9554e22aaa2ff8812219e376821c Mon Sep 17 00:00:00 2001 From: Bruce Date: Thu, 12 May 2022 13:37:09 +0800 Subject: [PATCH 3/5] feat: common params --- app/utils/http.ts | 36 ++-- server-v2/api/studio/etc/studio-api.yaml | 2 +- .../internal/logic/gateway/execngqllogic.go | 2 +- .../api/studio/internal/service/gateway.go | 23 +-- server-v2/api/studio/internal/types/types.go | 5 +- server-v2/api/studio/pkg/auth/authorize.go | 182 +++++++++--------- server-v2/api/studio/pkg/base/types.go | 2 +- server-v2/api/studio/pkg/ecode/codes.go | 6 + server-v2/api/studio/pkg/middleware/assets.go | 100 ---------- server-v2/api/studio/restapi/gateway.api | 5 +- server-v2/api/studio/studio.go | 6 +- server-v2/go.mod | 4 +- server-v2/go.sum | 2 + 13 files changed, 147 insertions(+), 228 deletions(-) delete mode 100644 server-v2/api/studio/pkg/middleware/assets.go diff --git a/app/utils/http.ts b/app/utils/http.ts index 2ca64362..eff4ee22 100644 --- a/app/utils/http.ts +++ b/app/utils/http.ts @@ -6,13 +6,17 @@ import intl from 'react-intl-universal'; import { getRootStore } from '@app/stores'; import { trackEvent } from './stat'; -const subErrMsgStr = [ - 'session expired', - 'connection refused', - 'broken pipe', - 'an existing connection was forcibly closed', - 'Token is expired', -]; +export enum HttpResCode { + ErrBadRequest = 40004000, + ErrParam = 40004001, + ErrUnauthorized = 40104000, + ErrSession = 40104001, + ErrForbidden = 40304000, + ErrNotFound = 40404000, + ErrInternalServer = 50004000, + ErrNotImplemented = 50104000, + ErrUnknown = 90004000, +} const service = axios.create({ transformResponse: [ @@ -39,24 +43,20 @@ service.interceptors.request.use(config => { service.interceptors.response.use( (response: any) => { - const { code, message: errMsg } = response.data; - const isConnectReq = /api-nebula\/db\/connect$/.test(response.config?.url); - // if connection refused, login again - if (code === -1 && new RegExp(subErrMsgStr.join('|')).test(errMsg)) { - message.warning(errMsg); - !isConnectReq && getRootStore().global.logout(); - } else if (code === -1 && errMsg) { - message.warning(errMsg); - } - return response.data; + const isExecReq = /api-nebula\/db\/(exec|batchExec)$/.test(response.config?.url); + return isExecReq ? response.data?.data : response.data; }, (error: any) => { - if (error.response && error.response.status) { + if (error.response?.status) { message.error( `${intl.get('common.requestError')}: ${error.response.status} ${ error.response.statusText }`, ); + // relogin + if (error.response.data?.code === HttpResCode.ErrSession) { + getRootStore().global.logout(); + } return error.response; } else { message.error(`${intl.get('common.requestError')}: ${error}`); diff --git a/server-v2/api/studio/etc/studio-api.yaml b/server-v2/api/studio/etc/studio-api.yaml index 6423c0ab..0d12cbb3 100644 --- a/server-v2/api/studio/etc/studio-api.yaml +++ b/server-v2/api/studio/etc/studio-api.yaml @@ -1,6 +1,6 @@ Name: studio-api Host: 0.0.0.0 -Port: 7002 +Port: 9000 MaxBytes: 1073741824 Debug: Enable: false diff --git a/server-v2/api/studio/internal/logic/gateway/execngqllogic.go b/server-v2/api/studio/internal/logic/gateway/execngqllogic.go index bc5244ee..f16f31b4 100644 --- a/server-v2/api/studio/internal/logic/gateway/execngqllogic.go +++ b/server-v2/api/studio/internal/logic/gateway/execngqllogic.go @@ -25,5 +25,5 @@ func NewExecNGQLLogic(ctx context.Context, svcCtx *svc.ServiceContext) ExecNGQLL } func (l *ExecNGQLLogic) ExecNGQL(req types.ExecNGQLParams) (resp *types.AnyResponse, err error) { - return service.NewGatewayService(l.ctx, l.svcCtx).GetExec(&req) + return service.NewGatewayService(l.ctx, l.svcCtx).ExecNGQL(&req) } diff --git a/server-v2/api/studio/internal/service/gateway.go b/server-v2/api/studio/internal/service/gateway.go index ca7be670..cf60be23 100644 --- a/server-v2/api/studio/internal/service/gateway.go +++ b/server-v2/api/studio/internal/service/gateway.go @@ -6,6 +6,7 @@ import ( "github.com/vesoft-inc/nebula-http-gateway/ccore/nebula/gateway/dao" "github.com/vesoft-inc/nebula-studio/server/api/studio/internal/svc" "github.com/vesoft-inc/nebula-studio/server/api/studio/internal/types" + "github.com/vesoft-inc/nebula-studio/server/api/studio/pkg/ecode" "github.com/zeromicro/go-zero/core/logx" ) @@ -14,7 +15,7 @@ var _ GatewayService = (*gatewayService)(nil) type ( GatewayService interface { - GetExec(request *types.ExecNGQLParams) (*types.AnyResponse, error) + ExecNGQL(request *types.ExecNGQLParams) (*types.AnyResponse, error) ConnectDB(request *types.ConnectDBParams) (*types.ConnectDBResult, error) DisconnectDB(request *types.DisconnectDBParams) (*types.AnyResponse, error) } @@ -34,14 +35,6 @@ func NewGatewayService(ctx context.Context, svcCtx *svc.ServiceContext) GatewayS } } -func (s *gatewayService) GetExec(request *types.ExecNGQLParams) (*types.AnyResponse, error) { - return &types.AnyResponse{ - Data: map[string]any{ - "message": "ok", - }, - }, nil -} - func (s *gatewayService) ConnectDB(request *types.ConnectDBParams) (*types.ConnectDBResult, error) { return &types.ConnectDBResult{ Version: string(request.NebulaVersion), @@ -49,8 +42,16 @@ func (s *gatewayService) ConnectDB(request *types.ConnectDBParams) (*types.Conne } func (s *gatewayService) DisconnectDB(request *types.DisconnectDBParams) (*types.AnyResponse, error) { - if request.Nsid != "" { - dao.Disconnect(request.Nsid) + if request.NSID != "" { + dao.Disconnect(request.NSID) } return nil, nil } + +func (s *gatewayService) ExecNGQL(request *types.ExecNGQLParams) (*types.AnyResponse, error) { + execute, _, err := dao.Execute(request.NSID, request.Gql, request.ParamList) + if err != nil { + return nil, ecode.WithCode(ecode.ErrInternalServer, err, "exec failed") + } + return &types.AnyResponse{Data: execute}, nil +} diff --git a/server-v2/api/studio/internal/types/types.go b/server-v2/api/studio/internal/types/types.go index e5d2d7fb..1e2072ea 100644 --- a/server-v2/api/studio/internal/types/types.go +++ b/server-v2/api/studio/internal/types/types.go @@ -7,7 +7,8 @@ type GetHealth struct { type ExecNGQLParams struct { Gql string `json:"gql"` - ParamList []string `json:"paramList"` + ParamList []string `json:"paramList,optional"` + NSID string `form:"NSID"` } type ConnectDBParams struct { @@ -22,7 +23,7 @@ type ConnectDBResult struct { } type DisconnectDBParams struct { - Nsid string `form:"nsid,optional"` + NSID string `form:"NSID,optional"` } type AnyResponse struct { diff --git a/server-v2/api/studio/pkg/auth/authorize.go b/server-v2/api/studio/pkg/auth/authorize.go index 21e86ce4..745df935 100644 --- a/server-v2/api/studio/pkg/auth/authorize.go +++ b/server-v2/api/studio/pkg/auth/authorize.go @@ -2,6 +2,7 @@ package auth import ( "encoding/base64" + "errors" "fmt" "net/http" "strings" @@ -11,6 +12,7 @@ import ( "github.com/vesoft-inc/nebula-http-gateway/ccore/nebula/gateway/dao" "github.com/vesoft-inc/nebula-http-gateway/ccore/nebula/gateway/pool" "github.com/vesoft-inc/nebula-studio/server/api/studio/internal/config" + "github.com/vesoft-inc/nebula-studio/server/api/studio/internal/svc" "github.com/vesoft-inc/nebula-studio/server/api/studio/internal/types" "github.com/vesoft-inc/nebula-studio/server/api/studio/pkg/ecode" "github.com/vesoft-inc/nebula-studio/server/api/studio/pkg/utils" @@ -22,18 +24,62 @@ type ( CtxKeyUserInfo struct{} AuthData struct { - NebulaAddress string `json:"nebulaAddress"` - Username string `json:"username"` - ClientID string `json:"clientID"` + Address string `json:"address"` + Port int `json:"port"` + Username string `json:"username"` } authClaims struct { *AuthData - jwt.StandardClaims + jwt.RegisteredClaims } ) -var globalConfig = new(config.Config) +func CreateToken(authData *AuthData, config *config.Config) (string, error) { + now := time.Now() + expiresAt := now.Add(time.Duration(config.Auth.AccessExpire) * time.Second).Unix() + + token := jwt.NewWithClaims(jwt.SigningMethodHS256, + authClaims{ + AuthData: authData, + RegisteredClaims: jwt.RegisteredClaims{ + ExpiresAt: &jwt.NumericDate{Time: time.Unix(expiresAt, 0)}, + // ExpiresAt: expiresAt, + }, + }) + + return token.SignedString([]byte(config.Auth.AccessSecret)) +} + +func Decode(tokenString, secret string) (*AuthData, error) { + auth := authClaims{} + token, err := jwt.ParseWithClaims(tokenString, &auth, func(token *jwt.Token) (interface{}, error) { + if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { + return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) + } + return []byte(secret), nil + }) + + if err != nil { + if ve, ok := err.(*jwt.ValidationError); ok { + if ve.Errors&jwt.ValidationErrorMalformed != 0 { + return nil, errors.New("that's not even a token") + } else if ve.Errors&jwt.ValidationErrorExpired != 0 { + return nil, errors.New("token is expired") + } else if ve.Errors&jwt.ValidationErrorNotValidYet != 0 { + return nil, errors.New("token not active yet") + } else { + return nil, errors.New("couldn't handle this token") + } + } + } + + if _, ok := token.Claims.(*authClaims); !ok || !token.Valid { + return nil, errors.New("couldn't handle this token") + } + + return auth.AuthData, nil +} func parseConnectDBParams(params *types.ConnectDBParams, config *config.Config) (string, *pool.ClientInfo, error) { tokenSplit := strings.Split(params.Authorization, " ") @@ -59,32 +105,20 @@ func parseConnectDBParams(params *types.ConnectDBParams, config *config.Config) tokenString, err := CreateToken( &AuthData{ - NebulaAddress: params.Address, - Username: username, + Address: params.Address, + Port: params.Port, + Username: username, }, config, ) return tokenString, clientInfo, err } -func CreateToken(authData *AuthData, config *config.Config) (string, error) { - now := time.Now() - expiresAt := now.Add(time.Duration(config.Auth.AccessExpire) * time.Second).Unix() - - token := jwt.NewWithClaims(jwt.SigningMethodHS256, - authClaims{ - AuthData: authData, - StandardClaims: jwt.StandardClaims{ - ExpiresAt: expiresAt, - }, - }) - - return token.SignedString([]byte(config.Auth.AccessSecret)) -} - -func AuthMiddlewareWithConfig(config *config.Config) rest.Middleware { +func AuthMiddlewareWithCtx(svcCtx *svc.ServiceContext) rest.Middleware { return func(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { + configAuth := svcCtx.Config.Auth + // login handler if strings.HasSuffix(r.URL.Path, "/connect") { var req types.ConnectDBParams @@ -95,7 +129,7 @@ func AuthMiddlewareWithConfig(config *config.Config) rest.Middleware { return } - tokenString, clientInfo, err := parseConnectDBParams(&req, config) + tokenString, clientInfo, err := parseConnectDBParams(&req, &svcCtx.Config) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return @@ -106,86 +140,60 @@ func AuthMiddlewareWithConfig(config *config.Config) rest.Middleware { Value: tokenString, Path: "/", HttpOnly: true, - MaxAge: 1800, + MaxAge: int(configAuth.AccessExpire), } - nsid := http.Cookie{ - Name: "nsid", + NSID := http.Cookie{ + Name: "NSID", Value: clientInfo.ClientID, Path: "/", HttpOnly: true, - MaxAge: 1800, + MaxAge: int(configAuth.AccessExpire), } utils.AddQueryParams(r, map[string]string{"nebulaVersion": string(clientInfo.NebulaVersion)}) - // w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Set("Set-Cookie", token.String()) - w.Header().Add("Set-Cookie", nsid.String()) - } else if strings.HasSuffix(r.URL.Path, "/disconnect") { - nsidCookie, err := r.Cookie("nsid") + w.Header().Add("Set-Cookie", NSID.String()) - if err == nil { - utils.AddQueryParams(r, map[string]string{"nsid": nsidCookie.Value}) - } - - w.Header().Set("Set-Cookie", utils.DisabledCookie("token").String()) - w.Header().Add("Set-Cookie", utils.DisabledCookie("nsid").String()) - } else { + next(w, r) + return } - next(w, r) - } - } -} - -func GenerateLoginToken(address string, port int, authorization string, config *config.Config) (string, error) { - tokenSplit := strings.Split(authorization, " ") - if len(tokenSplit) != 2 { - return "", fmt.Errorf("invalid authorization") - } - decode, err := base64.StdEncoding.DecodeString(tokenSplit[1]) - if err != nil { - return "", err - } + NSIDCookie, NSIDErr := r.Cookie("NSID") + if NSIDErr == nil { + // Add NSID to request query + utils.AddQueryParams(r, map[string]string{"NSID": NSIDCookie.Value}) + } - loginInfo := strings.Split(string(decode), ":") - if len(loginInfo) < 2 { - return "", fmt.Errorf("len of account is less than two") - } + if strings.HasSuffix(r.URL.Path, "/disconnect") { + w.Header().Set("Set-Cookie", utils.DisabledCookie("token").String()) + w.Header().Add("Set-Cookie", utils.DisabledCookie("NSID").String()) + next(w, r) + return + } - username, password := loginInfo[0], loginInfo[1] - clientInfo, err := dao.Connect(address, port, username, password) + tokenCookie, tokenErr := r.Cookie("token") + if NSIDErr != nil || tokenErr != nil { + if NSIDErr != nil { + svcCtx.ResponseHandler.Handle(w, r, nil, ecode.WithSessionMessage(NSIDErr)) + return + } + } - if err != nil { - return "", err - } + auth, authErr := Decode(tokenCookie.Value, configAuth.AccessSecret) + if authErr != nil { + svcCtx.ResponseHandler.Handle(w, r, nil, ecode.WithSessionMessage(authErr)) + return + } - return CreateToken( - &AuthData{ - NebulaAddress: address, - Username: username, - ClientID: clientInfo.ClientID, - }, - config, - ) -} + // Add address|port|username to request query + utils.AddQueryParams(r, map[string]string{ + "address": auth.Address, + "port": fmt.Sprintf("%d", auth.Port), + "username": auth.Username, + }) -func (d *AuthData) Decode(tokenString, secret string) error { - token, err := jwt.ParseWithClaims(tokenString, &authClaims{ - AuthData: d, - }, func(token *jwt.Token) (interface{}, error) { - if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { - return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) + next(w, r) } - return []byte(secret), nil - }) - if err != nil { - return err } - - if _, ok := token.Claims.(*authClaims); !ok || !token.Valid { - return fmt.Errorf("jwt parse not valid") - } - - return nil } diff --git a/server-v2/api/studio/pkg/base/types.go b/server-v2/api/studio/pkg/base/types.go index a1319637..ef6f1c6f 100644 --- a/server-v2/api/studio/pkg/base/types.go +++ b/server-v2/api/studio/pkg/base/types.go @@ -2,7 +2,7 @@ package base type StatusCode int -type Result any +type Result interface{} const ( Error StatusCode = -1 diff --git a/server-v2/api/studio/pkg/ecode/codes.go b/server-v2/api/studio/pkg/ecode/codes.go index 7b8770e4..37feb4ca 100644 --- a/server-v2/api/studio/pkg/ecode/codes.go +++ b/server-v2/api/studio/pkg/ecode/codes.go @@ -1,6 +1,7 @@ package ecode import ( + "fmt" "net/http" ) @@ -46,6 +47,11 @@ func WithUnauthorized(err error, formatWithArgs ...interface{}) error { return WithCode(ErrUnauthorized, err, formatWithArgs...) } +func WithSessionMessage(err error, formatWithArgs ...interface{}) error { + ErrSessionWithMessage := newErrCode(CCUnauthorized, PlatformCode, 1, fmt.Sprintf("ErrSession::%s", err.Error())) + return WithCode(ErrSessionWithMessage, err, formatWithArgs...) +} + func WithForbidden(err error, formatWithArgs ...interface{}) error { return WithCode(ErrForbidden, err, formatWithArgs...) } diff --git a/server-v2/api/studio/pkg/middleware/assets.go b/server-v2/api/studio/pkg/middleware/assets.go deleted file mode 100644 index a58a2abb..00000000 --- a/server-v2/api/studio/pkg/middleware/assets.go +++ /dev/null @@ -1,100 +0,0 @@ -package middleware - -import ( - "net/http" - "net/url" - "path/filepath" - "strings" -) - -type ( - AssertsConfig struct { - Prefix string - Root string - Index string - SPA bool - Filesystem http.FileSystem - } - - assertsHandler struct { - config AssertsConfig - } -) - -func NewAssertsHandler(config AssertsConfig) http.Handler { - if config.Root == "" { - config.Root = "." - } - if config.Index == "" { - config.Index = "/index.html" - } else { - config.Index = filepath.Clean("/" + config.Index) - } - if config.Filesystem == nil { - config.Filesystem = http.Dir(config.Root) - config.Root = "." - } - - return &assertsHandler{ - config: config, - } -} - -func (h *assertsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { - urlPath, err := url.PathUnescape(r.URL.Path) - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - - pathInFs := filepath.Clean("/" + urlPath) - if h.config.Prefix != "" { - if strings.HasPrefix(urlPath, h.config.Prefix) { - pathInFs = filepath.Clean("/" + urlPath[len(h.config.Prefix):]) - } else { - if !h.config.SPA { - http.NotFound(w, r) - return - } - pathInFs = h.config.Index - } - } - - file, err := h.config.Filesystem.Open(filepath.Join(h.config.Root, pathInFs)) - if err != nil { - if !h.config.SPA || urlPath == h.config.Index { - http.NotFound(w, r) - return - } - pathInFs = h.config.Index - file, err = h.config.Filesystem.Open(filepath.Join(h.config.Root, pathInFs)) - if err != nil { - http.NotFound(w, r) - return - } - } - defer file.Close() - - stat, err := file.Stat() - if err != nil { - http.NotFound(w, r) - return - } - - if stat.IsDir() { - pathInFs = filepath.Join(pathInFs, h.config.Index) - file, err = h.config.Filesystem.Open(filepath.Join(h.config.Root, pathInFs)) - if err != nil { - http.NotFound(w, r) - return - } - defer file.Close() - stat, err = file.Stat() - if err != nil || stat.IsDir() { - http.NotFound(w, r) - return - } - } - - http.ServeContent(w, r, stat.Name(), stat.ModTime(), file) -} diff --git a/server-v2/api/studio/restapi/gateway.api b/server-v2/api/studio/restapi/gateway.api index 733c0836..c4c7a1b5 100644 --- a/server-v2/api/studio/restapi/gateway.api +++ b/server-v2/api/studio/restapi/gateway.api @@ -3,7 +3,8 @@ syntax = "v1" type ( ExecNGQLParams { Gql string `json:"gql"` - ParamList []string `json:"paramList"` + ParamList []string `json:"paramList,optional"` + NSID string `form:"NSID"` } ConnectDBParams { Address string `json:"address"` @@ -15,7 +16,7 @@ type ( Version string `json:"version"` } DisconnectDBParams { - Nsid string `form:"nsid,optional"` + NSID string `form:"NSID,optional"` } AnyResponse { diff --git a/server-v2/api/studio/studio.go b/server-v2/api/studio/studio.go index 77ad786e..55a1258e 100644 --- a/server-v2/api/studio/studio.go +++ b/server-v2/api/studio/studio.go @@ -6,11 +6,11 @@ import ( "fmt" "net/http" + "github.com/vesoft-inc/go-pkg/middleware" "github.com/vesoft-inc/nebula-studio/server/api/studio/internal/config" "github.com/vesoft-inc/nebula-studio/server/api/studio/internal/handler" "github.com/vesoft-inc/nebula-studio/server/api/studio/internal/svc" "github.com/vesoft-inc/nebula-studio/server/api/studio/pkg/auth" - "github.com/vesoft-inc/nebula-studio/server/api/studio/pkg/middleware" "github.com/zeromicro/go-zero/core/conf" "github.com/zeromicro/go-zero/rest" @@ -28,7 +28,7 @@ func main() { conf.MustLoad(*configFile, &c, conf.UseEnv()) svcCtx := svc.NewServiceContext(c) - server := rest.MustNewServer(c.RestConf, rest.WithNotFoundHandler(middleware.NewAssertsHandler(middleware.AssertsConfig{ + server := rest.MustNewServer(c.RestConf, rest.WithNotFoundHandler(middleware.NewAssetsHandler(middleware.AssetsConfig{ Root: "assets", Filesystem: http.FS(embedAssets), SPA: true, @@ -37,7 +37,7 @@ func main() { defer server.Stop() // global middleware - server.Use(auth.AuthMiddlewareWithConfig(&c)) + server.Use(auth.AuthMiddlewareWithCtx(svcCtx)) // api handlers handler.RegisterHandlers(server, svcCtx) diff --git a/server-v2/go.mod b/server-v2/go.mod index 6cde3328..1bafbe9b 100644 --- a/server-v2/go.mod +++ b/server-v2/go.mod @@ -1,9 +1,9 @@ module github.com/vesoft-inc/nebula-studio/server -go 1.18 +go 1.17 require ( - github.com/vesoft-inc/go-pkg v0.0.0-20220415101753-97328ddf3e40 + github.com/vesoft-inc/go-pkg v0.0.0-20220511092334-a180a9379d8d github.com/zeromicro/go-zero v1.3.3 ) diff --git a/server-v2/go.sum b/server-v2/go.sum index 4abb9dd0..5f5a2248 100644 --- a/server-v2/go.sum +++ b/server-v2/go.sum @@ -391,6 +391,8 @@ github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhV github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= github.com/vesoft-inc/go-pkg v0.0.0-20220415101753-97328ddf3e40 h1:vBoIYZyRgxHFDaCc/4NfkH/rVjYNnDYqg8cMczWb7vU= github.com/vesoft-inc/go-pkg v0.0.0-20220415101753-97328ddf3e40/go.mod h1:HCAXRhF2io+nPLQnl+RQ6XyVcp1Xdv6NgslXRBBCiEU= +github.com/vesoft-inc/go-pkg v0.0.0-20220511092334-a180a9379d8d h1:Q/eVc0H8CuQNmviD43vAxtJKyB3aFd9R8Z7pOdxokoA= +github.com/vesoft-inc/go-pkg v0.0.0-20220511092334-a180a9379d8d/go.mod h1:HCAXRhF2io+nPLQnl+RQ6XyVcp1Xdv6NgslXRBBCiEU= github.com/vesoft-inc/nebula-http-gateway/ccore v0.0.0-20220413113447-a3f4c56287d8 h1:iL92Uk6hAe4vUBK/L99wf5295HYOtnD4plctVA5xek0= github.com/vesoft-inc/nebula-http-gateway/ccore v0.0.0-20220413113447-a3f4c56287d8/go.mod h1:sFEvE+cY4TgwqWx6H6msOqAUzRhsEHHKaaMgIZENHuQ= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= From f4bbbf8b5f98333ae3370adf03bf0fdeb453176e Mon Sep 17 00:00:00 2001 From: Bruce Date: Thu, 12 May 2022 13:46:40 +0800 Subject: [PATCH 4/5] update: go mod tidy --- server-v2/go.sum | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/server-v2/go.sum b/server-v2/go.sum index 5f5a2248..f6c28bf6 100644 --- a/server-v2/go.sum +++ b/server-v2/go.sum @@ -332,6 +332,7 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= +github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= @@ -387,10 +388,9 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= -github.com/vesoft-inc/go-pkg v0.0.0-20220415101753-97328ddf3e40 h1:vBoIYZyRgxHFDaCc/4NfkH/rVjYNnDYqg8cMczWb7vU= -github.com/vesoft-inc/go-pkg v0.0.0-20220415101753-97328ddf3e40/go.mod h1:HCAXRhF2io+nPLQnl+RQ6XyVcp1Xdv6NgslXRBBCiEU= github.com/vesoft-inc/go-pkg v0.0.0-20220511092334-a180a9379d8d h1:Q/eVc0H8CuQNmviD43vAxtJKyB3aFd9R8Z7pOdxokoA= github.com/vesoft-inc/go-pkg v0.0.0-20220511092334-a180a9379d8d/go.mod h1:HCAXRhF2io+nPLQnl+RQ6XyVcp1Xdv6NgslXRBBCiEU= github.com/vesoft-inc/nebula-http-gateway/ccore v0.0.0-20220413113447-a3f4c56287d8 h1:iL92Uk6hAe4vUBK/L99wf5295HYOtnD4plctVA5xek0= @@ -405,8 +405,6 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/gopher-lua v0.0.0-20200816102855-ee81675732da/go.mod h1:E1AXubJBdNmFERAOucpDIxNzeGfLzg0mYh+UfMWdChA= -github.com/zeromicro/go-zero v1.3.2 h1:2HcmceZDEGwZWvofCG+0GXyh+Gtz/wKCW4Fq8Mb7KIg= -github.com/zeromicro/go-zero v1.3.2/go.mod h1:DEj3Fwj1Ui1ltsgf6YqwTL9nD4+tYzIRX0c1pWtQo1E= github.com/zeromicro/go-zero v1.3.3 h1:6qv9PcfqfB1tMgp1FJlP1LfWSZ4XD+FwojvA2h5LL2k= github.com/zeromicro/go-zero v1.3.3/go.mod h1:LwuYc2V04ZHhWPWGJYQ+kJ5DT4QSkeaZGqXiQcpkfks= go.etcd.io/etcd/api/v3 v3.5.2/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A= @@ -528,7 +526,6 @@ golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20210917221730-978cfadd31cf/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220421235706-1d1ef9303861 h1:yssD99+7tqHWO5Gwh81phT+67hg+KttniBr6UnEXOY8= golang.org/x/net v0.0.0-20220421235706-1d1ef9303861/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= @@ -604,8 +601,6 @@ golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 h1:nhht2DYV/Sn3qOayu8lM+cU1ii9sTLUeBQwQQfUHtrs= -golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 h1:xHms4gcpe1YE7A3yIllJXP16CMAGuqwO2lX1mTyyRRc= golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= @@ -732,8 +727,6 @@ google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20220228195345-15d65a4533f7 h1:ntPPoHzFW6Xp09ueznmahONZufyoSakK/piXnr2BU3I= -google.golang.org/genproto v0.0.0-20220228195345-15d65a4533f7/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= google.golang.org/genproto v0.0.0-20220422154200-b37d22cd5731 h1:nquqdM9+ps0JZcIiI70+tqoaIFS5Ql4ZuK8UXnz3HfE= google.golang.org/genproto v0.0.0-20220422154200-b37d22cd5731/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= @@ -752,8 +745,6 @@ google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTp google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= -google.golang.org/grpc v1.44.0 h1:weqSxi/TMs1SqFRMHCtBgXRs8k3X39QIDEZ0pRcttUg= -google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= google.golang.org/grpc v1.46.0 h1:oCjezcn6g6A75TGoKYBPgKmVBLexhYLM6MebdrPApP8= google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= @@ -769,11 +760,11 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -782,6 +773,7 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw= gopkg.in/h2non/gock.v1 v1.1.2 h1:jBbHXgGBK/AoPVfJh5x4r/WxIrElvbLel8TCZkkZJoY= gopkg.in/h2non/gock.v1 v1.1.2/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaDva0= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= From ff97c23470bf609794c6db39da330c5deb96cf0c Mon Sep 17 00:00:00 2001 From: Bruce Date: Thu, 12 May 2022 18:59:30 +0800 Subject: [PATCH 5/5] feat: batchExec --- config/webpack.prod.js | 2 +- .../handler/gateway/batchexecngqlhandler.go | 33 +++++++++++++++++ .../api/studio/internal/handler/routes.go | 5 +++ .../logic/gateway/batchexecngqllogic.go | 29 +++++++++++++++ .../api/studio/internal/service/gateway.go | 37 +++++++++++++++++++ server-v2/api/studio/internal/types/types.go | 6 +++ server-v2/api/studio/pkg/auth/authorize.go | 17 ++++++--- server-v2/api/studio/pkg/base/types.go | 2 - server-v2/api/studio/restapi/gateway.api | 10 ++++- 9 files changed, 131 insertions(+), 10 deletions(-) create mode 100644 server-v2/api/studio/internal/handler/gateway/batchexecngqlhandler.go create mode 100644 server-v2/api/studio/internal/logic/gateway/batchexecngqllogic.go diff --git a/config/webpack.prod.js b/config/webpack.prod.js index e0f19da4..2fe7b62f 100644 --- a/config/webpack.prod.js +++ b/config/webpack.prod.js @@ -13,7 +13,7 @@ const publicConfig = { path: path.join(__dirname, '../dist/'), filename: '[name].[chunkhash].js', chunkFilename: '[name].[contenthash].js', - publicPath: '/assets/', + publicPath: '/', }, module: { rules: [ diff --git a/server-v2/api/studio/internal/handler/gateway/batchexecngqlhandler.go b/server-v2/api/studio/internal/handler/gateway/batchexecngqlhandler.go new file mode 100644 index 00000000..56338b7f --- /dev/null +++ b/server-v2/api/studio/internal/handler/gateway/batchexecngqlhandler.go @@ -0,0 +1,33 @@ +// Code generated by goctl. DO NOT EDIT. +package gateway + +import ( + "net/http" + + "github.com/vesoft-inc/go-pkg/validator" + "github.com/vesoft-inc/nebula-studio/server/api/studio/pkg/ecode" + + "github.com/vesoft-inc/nebula-studio/server/api/studio/internal/logic/gateway" + "github.com/vesoft-inc/nebula-studio/server/api/studio/internal/svc" + "github.com/vesoft-inc/nebula-studio/server/api/studio/internal/types" + "github.com/zeromicro/go-zero/rest/httpx" +) + +func BatchExecNGQLHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req types.BatchExecNGQLParams + if err := httpx.Parse(r, &req); err != nil { + err = ecode.WithCode(ecode.ErrParam, err) + svcCtx.ResponseHandler.Handle(w, r, nil, err) + return + } + if err := validator.Struct(req); err != nil { + svcCtx.ResponseHandler.Handle(w, r, nil, err) + return + } + + l := gateway.NewBatchExecNGQLLogic(r.Context(), svcCtx) + data, err := l.BatchExecNGQL(req) + svcCtx.ResponseHandler.Handle(w, r, data, err) + } +} diff --git a/server-v2/api/studio/internal/handler/routes.go b/server-v2/api/studio/internal/handler/routes.go index 3ac7849a..c6b5e76d 100644 --- a/server-v2/api/studio/internal/handler/routes.go +++ b/server-v2/api/studio/internal/handler/routes.go @@ -30,6 +30,11 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { Path: "/exec", Handler: gateway.ExecNGQLHandler(serverCtx), }, + { + Method: http.MethodPost, + Path: "/batchExec", + Handler: gateway.BatchExecNGQLHandler(serverCtx), + }, }, rest.WithPrefix("/api-nebula/db"), ) diff --git a/server-v2/api/studio/internal/logic/gateway/batchexecngqllogic.go b/server-v2/api/studio/internal/logic/gateway/batchexecngqllogic.go new file mode 100644 index 00000000..9d9d52f0 --- /dev/null +++ b/server-v2/api/studio/internal/logic/gateway/batchexecngqllogic.go @@ -0,0 +1,29 @@ +package gateway + +import ( + "context" + + "github.com/vesoft-inc/nebula-studio/server/api/studio/internal/service" + "github.com/vesoft-inc/nebula-studio/server/api/studio/internal/svc" + "github.com/vesoft-inc/nebula-studio/server/api/studio/internal/types" + + "github.com/zeromicro/go-zero/core/logx" +) + +type BatchExecNGQLLogic struct { + logx.Logger + ctx context.Context + svcCtx *svc.ServiceContext +} + +func NewBatchExecNGQLLogic(ctx context.Context, svcCtx *svc.ServiceContext) BatchExecNGQLLogic { + return BatchExecNGQLLogic{ + Logger: logx.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + } +} + +func (l *BatchExecNGQLLogic) BatchExecNGQL(req types.BatchExecNGQLParams) (*types.AnyResponse, error) { + return service.NewGatewayService(l.ctx, l.svcCtx).BatchExecNGQL(&req) +} diff --git a/server-v2/api/studio/internal/service/gateway.go b/server-v2/api/studio/internal/service/gateway.go index cf60be23..fdbc6218 100644 --- a/server-v2/api/studio/internal/service/gateway.go +++ b/server-v2/api/studio/internal/service/gateway.go @@ -2,10 +2,12 @@ package service import ( "context" + "strings" "github.com/vesoft-inc/nebula-http-gateway/ccore/nebula/gateway/dao" "github.com/vesoft-inc/nebula-studio/server/api/studio/internal/svc" "github.com/vesoft-inc/nebula-studio/server/api/studio/internal/types" + "github.com/vesoft-inc/nebula-studio/server/api/studio/pkg/base" "github.com/vesoft-inc/nebula-studio/server/api/studio/pkg/ecode" "github.com/zeromicro/go-zero/core/logx" @@ -16,6 +18,7 @@ var _ GatewayService = (*gatewayService)(nil) type ( GatewayService interface { ExecNGQL(request *types.ExecNGQLParams) (*types.AnyResponse, error) + BatchExecNGQL(request *types.BatchExecNGQLParams) (*types.AnyResponse, error) ConnectDB(request *types.ConnectDBParams) (*types.ConnectDBResult, error) DisconnectDB(request *types.DisconnectDBParams) (*types.AnyResponse, error) } @@ -55,3 +58,37 @@ func (s *gatewayService) ExecNGQL(request *types.ExecNGQLParams) (*types.AnyResp } return &types.AnyResponse{Data: execute}, nil } + +func (s *gatewayService) BatchExecNGQL(request *types.BatchExecNGQLParams) (*types.AnyResponse, error) { + data := make([]map[string]interface{}, 0) + + NSID := request.NSID + gqls := request.Gqls + paramList := request.ParamList + + for _, gql := range gqls { + execute, _, err := dao.Execute(NSID, gql, make([]string, 0)) + gqlRes := map[string]interface{}{"gql": gql, "data": execute} + if err != nil { + gqlRes["message"] = err.Error() + gqlRes["code"] = base.Error + } else { + gqlRes["code"] = base.Success + } + data = append(data, gqlRes) + } + + if len(paramList) > 0 { + execute, _, err := dao.Execute(NSID, "", paramList) + gqlRes := map[string]interface{}{"gql": strings.Join(paramList, "; "), "data": execute} + if err != nil { + gqlRes["message"] = err.Error() + gqlRes["code"] = base.Error + } else { + gqlRes["code"] = base.Success + } + data = append(data, gqlRes) + } + + return &types.AnyResponse{Data: data}, nil +} diff --git a/server-v2/api/studio/internal/types/types.go b/server-v2/api/studio/internal/types/types.go index 1e2072ea..ee697dba 100644 --- a/server-v2/api/studio/internal/types/types.go +++ b/server-v2/api/studio/internal/types/types.go @@ -11,6 +11,12 @@ type ExecNGQLParams struct { NSID string `form:"NSID"` } +type BatchExecNGQLParams struct { + Gqls []string `json:"gqls"` + ParamList []string `json:"paramList,optional"` + NSID string `form:"NSID"` +} + type ConnectDBParams struct { Address string `json:"address"` Port int `json:"port"` diff --git a/server-v2/api/studio/pkg/auth/authorize.go b/server-v2/api/studio/pkg/auth/authorize.go index 745df935..6cc6bced 100644 --- a/server-v2/api/studio/pkg/auth/authorize.go +++ b/server-v2/api/studio/pkg/auth/authorize.go @@ -35,6 +35,11 @@ type ( } ) +var ( + tokenName = "explorer_token" + nsidName = "explorer_nsid" +) + func CreateToken(authData *AuthData, config *config.Config) (string, error) { now := time.Now() expiresAt := now.Add(time.Duration(config.Auth.AccessExpire) * time.Second).Unix() @@ -136,14 +141,14 @@ func AuthMiddlewareWithCtx(svcCtx *svc.ServiceContext) rest.Middleware { } token := http.Cookie{ - Name: "token", + Name: tokenName, Value: tokenString, Path: "/", HttpOnly: true, MaxAge: int(configAuth.AccessExpire), } NSID := http.Cookie{ - Name: "NSID", + Name: nsidName, Value: clientInfo.ClientID, Path: "/", HttpOnly: true, @@ -159,20 +164,20 @@ func AuthMiddlewareWithCtx(svcCtx *svc.ServiceContext) rest.Middleware { return } - NSIDCookie, NSIDErr := r.Cookie("NSID") + NSIDCookie, NSIDErr := r.Cookie(nsidName) if NSIDErr == nil { // Add NSID to request query utils.AddQueryParams(r, map[string]string{"NSID": NSIDCookie.Value}) } if strings.HasSuffix(r.URL.Path, "/disconnect") { - w.Header().Set("Set-Cookie", utils.DisabledCookie("token").String()) - w.Header().Add("Set-Cookie", utils.DisabledCookie("NSID").String()) + w.Header().Set("Set-Cookie", utils.DisabledCookie(tokenName).String()) + w.Header().Add("Set-Cookie", utils.DisabledCookie(nsidName).String()) next(w, r) return } - tokenCookie, tokenErr := r.Cookie("token") + tokenCookie, tokenErr := r.Cookie(tokenName) if NSIDErr != nil || tokenErr != nil { if NSIDErr != nil { svcCtx.ResponseHandler.Handle(w, r, nil, ecode.WithSessionMessage(NSIDErr)) diff --git a/server-v2/api/studio/pkg/base/types.go b/server-v2/api/studio/pkg/base/types.go index ef6f1c6f..de47fbb6 100644 --- a/server-v2/api/studio/pkg/base/types.go +++ b/server-v2/api/studio/pkg/base/types.go @@ -7,6 +7,4 @@ type Result interface{} const ( Error StatusCode = -1 Success StatusCode = 0 - // TODO: need to del it - AuthorizationError StatusCode = 401 ) diff --git a/server-v2/api/studio/restapi/gateway.api b/server-v2/api/studio/restapi/gateway.api index c4c7a1b5..baee6de6 100644 --- a/server-v2/api/studio/restapi/gateway.api +++ b/server-v2/api/studio/restapi/gateway.api @@ -6,6 +6,11 @@ type ( ParamList []string `json:"paramList,optional"` NSID string `form:"NSID"` } + BatchExecNGQLParams { + Gqls []string `json:"gqls"` + ParamList []string `json:"paramList,optional"` + NSID string `form:"NSID"` + } ConnectDBParams { Address string `json:"address"` Port int `json:"port"` @@ -18,7 +23,6 @@ type ( DisconnectDBParams { NSID string `form:"NSID,optional"` } - AnyResponse { Data interface{} `json:"data"` } @@ -33,6 +37,10 @@ service studio-api { @doc "Exec NGQL" @handler ExecNGQL post /exec(ExecNGQLParams) returns (AnyResponse) + + @doc "BatchExec NGQL" + @handler BatchExecNGQL + post /batchExec(BatchExecNGQLParams) returns (AnyResponse) } @server(