From 7076985898d294a73c42d24baf6fe9d09010321c Mon Sep 17 00:00:00 2001 From: marvzhang Date: Fri, 3 Jan 2020 13:53:06 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8A=A0=E5=85=A5Node.js=E4=BE=9D=E8=B5=96?= =?UTF-8?q?=E5=AE=89=E8=A3=85=E5=92=8C=E8=AF=AD=E8=A8=80=E5=AE=89=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/constants/system.go | 2 +- backend/routes/system.go | 90 +++++++++++++++++- backend/scripts/install-nodejs.sh | 17 ++++ backend/services/rpc.go | 24 ++++- backend/services/system.go | 150 +++++++++++++++++++++++++++++- 5 files changed, 278 insertions(+), 5 deletions(-) create mode 100644 backend/scripts/install-nodejs.sh diff --git a/backend/constants/system.go b/backend/constants/system.go index 70d41063..bec8b8c5 100644 --- a/backend/constants/system.go +++ b/backend/constants/system.go @@ -8,6 +8,6 @@ const ( const ( Python = "python" - NodeJS = "node" + Nodejs = "node" Java = "java" ) diff --git a/backend/routes/system.go b/backend/routes/system.go index 0a388d0f..b4e130a9 100644 --- a/backend/routes/system.go +++ b/backend/routes/system.go @@ -32,6 +32,13 @@ func GetDepList(c *gin.Context) { return } depList = list + } else if lang == constants.Nodejs { + list, err := services.GetNodejsDepList(nodeId, depName) + if err != nil { + HandleError(http.StatusInternalServerError, c, err) + return + } + depList = list } else { HandleErrorF(http.StatusBadRequest, c, fmt.Sprintf("%s is not implemented", lang)) return @@ -64,6 +71,22 @@ func GetInstalledDepList(c *gin.Context) { } depList = list } + } else if lang == constants.Nodejs { + if services.IsMasterNode(nodeId) { + list, err := services.GetNodejsLocalInstalledDepList(nodeId) + if err != nil { + HandleError(http.StatusInternalServerError, c, err) + return + } + depList = list + } else { + list, err := services.GetNodejsRemoteInstalledDepList(nodeId) + if err != nil { + HandleError(http.StatusInternalServerError, c, err) + return + } + depList = list + } } else { HandleErrorF(http.StatusBadRequest, c, fmt.Sprintf("%s is not implemented", lang)) return @@ -129,6 +152,7 @@ func InstallDep(c *gin.Context) { var reqBody ReqBody if err := c.ShouldBindJSON(&reqBody); err != nil { HandleError(http.StatusBadRequest, c, err) + return } if reqBody.Lang == constants.Python { @@ -145,6 +169,20 @@ func InstallDep(c *gin.Context) { return } } + } else if reqBody.Lang == constants.Nodejs { + if services.IsMasterNode(nodeId) { + _, err := services.InstallNodejsLocalDep(reqBody.DepName) + if err != nil { + HandleError(http.StatusInternalServerError, c, err) + return + } + } else { + _, err := services.InstallNodejsRemoteDep(nodeId, reqBody.DepName) + if err != nil { + HandleError(http.StatusInternalServerError, c, err) + return + } + } } else { HandleErrorF(http.StatusBadRequest, c, fmt.Sprintf("%s is not implemented", reqBody.Lang)) return @@ -185,6 +223,20 @@ func UninstallDep(c *gin.Context) { return } } + } else if reqBody.Lang == constants.Nodejs { + if services.IsMasterNode(nodeId) { + _, err := services.UninstallNodejsLocalDep(reqBody.DepName) + if err != nil { + HandleError(http.StatusInternalServerError, c, err) + return + } + } else { + _, err := services.UninstallNodejsRemoteDep(nodeId, reqBody.DepName) + if err != nil { + HandleError(http.StatusInternalServerError, c, err) + return + } + } } else { HandleErrorF(http.StatusBadRequest, c, fmt.Sprintf("%s is not implemented", reqBody.Lang)) return @@ -223,5 +275,41 @@ func GetDepJson(c *gin.Context) { } func InstallLang(c *gin.Context) { - //lang := c.Query("lang") + type ReqBody struct { + Lang string `json:"lang"` + } + + nodeId := c.Param("id") + + var reqBody ReqBody + if err := c.ShouldBindJSON(&reqBody); err != nil { + HandleError(http.StatusBadRequest, c, err) + return + } + + if reqBody.Lang == constants.Nodejs { + if services.IsMasterNode(nodeId) { + _, err := services.InstallNodejsLocalLang() + if err != nil { + HandleError(http.StatusInternalServerError, c, err) + return + } + } else { + _, err := services.InstallNodejsRemoteLang(nodeId) + if err != nil { + HandleError(http.StatusInternalServerError, c, err) + return + } + } + } else { + HandleErrorF(http.StatusBadRequest, c, fmt.Sprintf("%s is not implemented", reqBody.Lang)) + return + } + + // TODO: check if install is successful + + c.JSON(http.StatusOK, Response{ + Status: "ok", + Message: "success", + }) } diff --git a/backend/scripts/install-nodejs.sh b/backend/scripts/install-nodejs.sh new file mode 100644 index 00000000..1ca73b2d --- /dev/null +++ b/backend/scripts/install-nodejs.sh @@ -0,0 +1,17 @@ +#!/bin/env bash + +# install nvm +curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.2/install.sh | bash +export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")" +[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm + +# install Node.js v8.12 +nvm install 8.12 + +# create soft links +ln -s $HOME/.nvm/versions/node/v8.12.0/bin/npm /usr/local/bin/npm +ln -s $HOME/.nvm/versions/node/v8.12.0/bin/node /usr/local/bin/node + +# environments manipulation +export NODE_PATH=$HOME.nvm/versions/node/v8.12.0/lib/node_modules +export PATH=$NODE_PATH:$PATH \ No newline at end of file diff --git a/backend/services/rpc.go b/backend/services/rpc.go index 77d0c217..9b6a5b74 100644 --- a/backend/services/rpc.go +++ b/backend/services/rpc.go @@ -21,10 +21,28 @@ type RpcMessage struct { } func RpcServerInstallLang(msg RpcMessage) RpcMessage { - // install dep rpc + lang := GetRpcParam("lang", msg.Params) + if lang == constants.Nodejs { + output, _ := InstallNodejsLocalLang() + msg.Result = output + } return msg } +func RpcClientInstallLang(nodeId string, lang string) (output string, err error) { + params := map[string]string{} + params["lang"] = lang + + data, err := RpcClientFunc(nodeId, constants.RpcInstallLang, params, 600)() + if err != nil { + return + } + + output = data + + return +} + func RpcServerInstallDep(msg RpcMessage) RpcMessage { lang := GetRpcParam("lang", msg.Params) depName := GetRpcParam("dep_name", msg.Params) @@ -81,6 +99,10 @@ func RpcServerGetInstalledDepList(nodeId string, msg RpcMessage) RpcMessage { depList, _ := GetPythonLocalInstalledDepList(nodeId) resultStr, _ := json.Marshal(depList) msg.Result = string(resultStr) + } else if lang == constants.Nodejs { + depList, _ := GetNodejsLocalInstalledDepList(nodeId) + resultStr, _ := json.Marshal(depList) + msg.Result = string(resultStr) } return msg } diff --git a/backend/services/system.go b/backend/services/system.go index 759f52e0..dce309d1 100644 --- a/backend/services/system.go +++ b/backend/services/system.go @@ -13,6 +13,7 @@ import ( "github.com/apex/log" "github.com/imroc/req" "os/exec" + "path" "regexp" "runtime/debug" "sort" @@ -65,8 +66,8 @@ func GetSystemInfo(nodeId string) (sysInfo entity.SystemInfo, err error) { func GetLangList(nodeId string) []entity.Lang { list := []entity.Lang{ {Name: "Python", ExecutableName: "python", ExecutablePath: "/usr/local/bin/python", DepExecutablePath: "/usr/local/bin/pip"}, - {Name: "NodeJS", ExecutableName: "node", ExecutablePath: "/usr/local/bin/node"}, - {Name: "Java", ExecutableName: "java", ExecutablePath: "/usr/local/bin/java"}, + {Name: "Node.js", ExecutableName: "node", ExecutablePath: "/usr/local/bin/node", DepExecutablePath: "/usr/local/bin/npm"}, + //{Name: "Java", ExecutableName: "java", ExecutablePath: "/usr/local/bin/java"}, } for i, lang := range list { list[i].Installed = IsInstalledLang(nodeId, lang) @@ -433,3 +434,148 @@ func UninstallPythonRemoteDep(nodeId string, depName string) (string, error) { // ============== // Node.js // ============== + +func InstallNodejsLocalLang() (string, error) { + cmd := exec.Command("/bin/sh", path.Join("scripts", "install-nodejs.sh")) + output, err := cmd.Output() + if err != nil { + log.Error(err.Error()) + debug.PrintStack() + return string(output), err + } + + // TODO: check if Node.js is installed successfully + + return string(output), nil +} + +// 获取Node.js远端依赖列表 +func InstallNodejsRemoteLang(nodeId string) (string, error) { + output, err := RpcClientInstallLang(nodeId, constants.Nodejs) + if err != nil { + return output, err + } + return output, nil +} + +// 获取Nodejs本地已安装的依赖列表 +func GetNodejsLocalInstalledDepList(nodeId string) ([]entity.Dependency, error) { + var list []entity.Dependency + + lang := GetLangFromLangName(nodeId, constants.Nodejs) + if !IsInstalledLang(nodeId, lang) { + return list, errors.New("nodejs is not installed") + } + cmd := exec.Command("npm", "ls", "-g", "--depth", "0") + outputBytes, _ := cmd.Output() + //if err != nil { + // log.Error("error: " + string(outputBytes)) + // debug.PrintStack() + // return list, err + //} + + regex := regexp.MustCompile("\\s(.*)@(.*)") + for _, line := range strings.Split(string(outputBytes), "\n") { + arr := regex.FindStringSubmatch(line) + if len(arr) < 3 { + continue + } + dep := entity.Dependency{ + Name: strings.ToLower(arr[1]), + Version: arr[2], + Installed: true, + } + list = append(list, dep) + } + + return list, nil +} + +// 获取Nodejs远端依赖列表 +func GetNodejsRemoteInstalledDepList(nodeId string) ([]entity.Dependency, error) { + depList, err := RpcClientGetInstalledDepList(nodeId, constants.Nodejs) + if err != nil { + return depList, err + } + return depList, nil +} + +// 安装Nodejs本地依赖 +func InstallNodejsLocalDep(depName string) (string, error) { + // 依赖镜像URL + url := "https://registry.npm.taobao.org" + + cmd := exec.Command("npm", "install", depName, "-g", "--registry", url) + outputBytes, err := cmd.Output() + if err != nil { + log.Errorf(err.Error()) + debug.PrintStack() + return fmt.Sprintf("error: %s", err.Error()), err + } + return string(outputBytes), nil +} + +// 获取Nodejs远端依赖列表 +func InstallNodejsRemoteDep(nodeId string, depName string) (string, error) { + output, err := RpcClientInstallDep(nodeId, constants.Nodejs, depName) + if err != nil { + return output, err + } + return output, nil +} + +// 安装Nodejs本地依赖 +func UninstallNodejsLocalDep(depName string) (string, error) { + cmd := exec.Command("npm", "uninstall", depName, "-g") + outputBytes, err := cmd.Output() + if err != nil { + log.Errorf(err.Error()) + debug.PrintStack() + return fmt.Sprintf("error: %s", err.Error()), err + } + return string(outputBytes), nil +} + +// 获取Nodejs远端依赖列表 +func UninstallNodejsRemoteDep(nodeId string, depName string) (string, error) { + output, err := RpcClientUninstallDep(nodeId, constants.Nodejs, depName) + if err != nil { + return output, err + } + return output, nil +} + +// 获取Nodejs本地依赖列表 +func GetNodejsDepList(nodeId string, searchDepName string) (depList []entity.Dependency, err error) { + // 执行shell命令 + cmd := exec.Command("npm", "search", "--json", searchDepName) + outputBytes, _ := cmd.Output() + + // 获取已安装依赖列表 + var installedDepList []entity.Dependency + if IsMasterNode(nodeId) { + installedDepList, err = GetNodejsLocalInstalledDepList(nodeId) + if err != nil { + return depList, err + } + } else { + installedDepList, err = GetNodejsRemoteInstalledDepList(nodeId) + if err != nil { + return depList, err + } + } + + // 反序列化 + if err := json.Unmarshal(outputBytes, &depList); err != nil { + log.Errorf(err.Error()) + debug.PrintStack() + return depList, err + } + + // 遍历安装列表 + for i, dep := range depList { + depList[i].Installed = IsInstalledDep(installedDepList, dep) + } + + return depList, nil +}