mirror of
https://github.com/crawlab-team/crawlab.git
synced 2026-01-21 17:21:09 +01:00
优化安装界面
This commit is contained in:
@@ -6,4 +6,5 @@ const (
|
||||
RpcUninstallDep = "uninstall_dep"
|
||||
RpcGetDepList = "get_dep_list"
|
||||
RpcGetInstalledDepList = "get_installed_dep_list"
|
||||
RpcGetLang = "get_lang"
|
||||
)
|
||||
|
||||
@@ -11,3 +11,10 @@ const (
|
||||
Nodejs = "node"
|
||||
Java = "java"
|
||||
)
|
||||
|
||||
const (
|
||||
InstallStatusNotInstalled = "not-installed"
|
||||
InstallStatusInstalling = "installing"
|
||||
InstallStatusInstallingOther = "installing-other"
|
||||
InstallStatusInstalled = "installed"
|
||||
)
|
||||
|
||||
@@ -20,6 +20,9 @@ type NodeMessage struct {
|
||||
// 爬虫相关
|
||||
SpiderId string `json:"spider_id"` //爬虫ID
|
||||
|
||||
// 语言相关
|
||||
Lang Lang `json:"lang"`
|
||||
|
||||
// 错误相关
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
@@ -19,7 +19,8 @@ type Lang struct {
|
||||
ExecutableName string `json:"executable_name"`
|
||||
ExecutablePaths []string `json:"executable_paths"`
|
||||
DepExecutablePath string `json:"dep_executable_path"`
|
||||
Installed bool `json:"installed"`
|
||||
LockPath string `json:"lock_path"`
|
||||
InstallStatus string `json:"install_status"`
|
||||
}
|
||||
|
||||
type Dependency struct {
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#!/bin/bash
|
||||
|
||||
# lock global
|
||||
touch /tmp/install.lock
|
||||
|
||||
# lock
|
||||
touch /tmp/install-java.lock
|
||||
|
||||
@@ -11,3 +14,6 @@ ln -s /usr/bin/java /usr/local/bin/java
|
||||
|
||||
# unlock
|
||||
rm /tmp/install-java.lock
|
||||
|
||||
# unlock global
|
||||
rm /tmp/install.lock
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#!/bin/bash
|
||||
|
||||
# lock global
|
||||
touch /tmp/install.lock
|
||||
|
||||
# lock
|
||||
touch /tmp/install-nodejs.lock
|
||||
|
||||
@@ -39,3 +42,6 @@ npm install puppeteer-chromium-resolver crawlab-sdk -g --unsafe-perm=true --regi
|
||||
|
||||
# unlock
|
||||
rm /tmp/install-nodejs.lock
|
||||
|
||||
# unlock global
|
||||
rm /tmp/install.lock
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#/bin/bash
|
||||
#!/bin/bash
|
||||
|
||||
# install node.js
|
||||
if [ "${CRAWLAB_SERVER_LANG_NODE}" = "Y" ];
|
||||
|
||||
@@ -21,6 +21,8 @@ type RpcMessage struct {
|
||||
Result string `json:"result"`
|
||||
}
|
||||
|
||||
// ========安装语言========
|
||||
|
||||
func RpcServerInstallLang(msg RpcMessage) RpcMessage {
|
||||
lang := GetRpcParam("lang", msg.Params)
|
||||
if lang == constants.Nodejs {
|
||||
@@ -34,6 +36,7 @@ func RpcClientInstallLang(nodeId string, lang string) (output string, err error)
|
||||
params := map[string]string{}
|
||||
params["lang"] = lang
|
||||
|
||||
// 发起 RPC 请求并阻塞,获取服务端数据
|
||||
data, err := RpcClientFunc(nodeId, constants.RpcInstallLang, params, 600)()
|
||||
if err != nil {
|
||||
return
|
||||
@@ -44,6 +47,41 @@ func RpcClientInstallLang(nodeId string, lang string) (output string, err error)
|
||||
return
|
||||
}
|
||||
|
||||
// ========./安装语言========
|
||||
|
||||
// ========获取语言========
|
||||
|
||||
func RpcServerGetLang(msg RpcMessage) RpcMessage {
|
||||
langName := GetRpcParam("lang", msg.Params)
|
||||
lang := GetLangFromLangNamePlain(langName)
|
||||
|
||||
// 序列化
|
||||
resultStr, _ := json.Marshal(lang)
|
||||
msg.Result = string(resultStr)
|
||||
return msg
|
||||
}
|
||||
|
||||
func RpcClientGetLang(nodeId string, langName string) (lang entity.Lang, err error) {
|
||||
params := map[string]string{}
|
||||
params["lang"] = langName
|
||||
|
||||
data, err := RpcClientFunc(nodeId, constants.RpcGetLang, params, 10)()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 反序列化结果
|
||||
if err := json.Unmarshal([]byte(data), &lang); err != nil {
|
||||
return lang, err
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// ========./获取语言========
|
||||
|
||||
// ========安装依赖========
|
||||
|
||||
func RpcServerInstallDep(msg RpcMessage) RpcMessage {
|
||||
lang := GetRpcParam("lang", msg.Params)
|
||||
depName := GetRpcParam("dep_name", msg.Params)
|
||||
@@ -69,6 +107,10 @@ func RpcClientInstallDep(nodeId string, lang string, depName string) (output str
|
||||
return
|
||||
}
|
||||
|
||||
// ========./安装依赖========
|
||||
|
||||
// ========卸载依赖========
|
||||
|
||||
func RpcServerUninstallDep(msg RpcMessage) RpcMessage {
|
||||
lang := GetRpcParam("lang", msg.Params)
|
||||
depName := GetRpcParam("dep_name", msg.Params)
|
||||
@@ -94,6 +136,10 @@ func RpcClientUninstallDep(nodeId string, lang string, depName string) (output s
|
||||
return
|
||||
}
|
||||
|
||||
// ========./卸载依赖========
|
||||
|
||||
// ========获取已安装依赖列表========
|
||||
|
||||
func RpcServerGetInstalledDepList(nodeId string, msg RpcMessage) RpcMessage {
|
||||
lang := GetRpcParam("lang", msg.Params)
|
||||
if lang == constants.Python {
|
||||
@@ -125,6 +171,9 @@ func RpcClientGetInstalledDepList(nodeId string, lang string) (list []entity.Dep
|
||||
return
|
||||
}
|
||||
|
||||
// ========./获取已安装依赖列表========
|
||||
|
||||
// RPC 客户端函数
|
||||
func RpcClientFunc(nodeId string, method string, params map[string]string, timeout int) func() (string, error) {
|
||||
return func() (result string, err error) {
|
||||
// 请求ID
|
||||
@@ -159,10 +208,12 @@ func RpcClientFunc(nodeId string, method string, params map[string]string, timeo
|
||||
}
|
||||
}
|
||||
|
||||
// 获取 RPC 参数
|
||||
func GetRpcParam(key string, params map[string]string) string {
|
||||
return params[key]
|
||||
}
|
||||
|
||||
// Object 转化为 String
|
||||
func ObjectToString(params interface{}) string {
|
||||
bytes, _ := json.Marshal(params)
|
||||
return utils.BytesToString(bytes)
|
||||
@@ -174,6 +225,7 @@ func StopRpcService() {
|
||||
IsRpcStopped = true
|
||||
}
|
||||
|
||||
// 初始化 RPC 服务
|
||||
func InitRpcService() error {
|
||||
go func() {
|
||||
for {
|
||||
@@ -213,6 +265,8 @@ func InitRpcService() error {
|
||||
replyMsg = RpcServerInstallLang(msg)
|
||||
} else if msg.Method == constants.RpcGetInstalledDepList {
|
||||
replyMsg = RpcServerGetInstalledDepList(node.Id.Hex(), msg)
|
||||
} else if msg.Method == constants.RpcGetLang {
|
||||
replyMsg = RpcServerGetLang(msg)
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -62,19 +62,48 @@ func GetSystemInfo(nodeId string) (sysInfo entity.SystemInfo, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func getLangList() []entity.Lang {
|
||||
list := []entity.Lang{
|
||||
{
|
||||
Name: "Python",
|
||||
ExecutableName: "python",
|
||||
ExecutablePaths: []string{"/usr/bin/python", "/usr/local/bin/python"},
|
||||
DepExecutablePath: "/usr/local/bin/pip",
|
||||
LockPath: "/tmp/install-python.lock",
|
||||
},
|
||||
{
|
||||
Name: "Node.js",
|
||||
ExecutableName: "node",
|
||||
ExecutablePaths: []string{"/usr/bin/node", "/usr/local/bin/node"},
|
||||
DepExecutablePath: "/usr/local/bin/npm",
|
||||
LockPath: "/tmp/install-nodejs.lock",
|
||||
},
|
||||
{
|
||||
Name: "Java",
|
||||
ExecutableName: "java",
|
||||
ExecutablePaths: []string{"/usr/bin/java", "/usr/local/bin/java"},
|
||||
LockPath: "/tmp/install-java.lock",
|
||||
},
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
// 获取语言列表
|
||||
func GetLangList(nodeId string) []entity.Lang {
|
||||
list := []entity.Lang{
|
||||
{Name: "Python", ExecutableName: "python", ExecutablePaths: []string{"/usr/bin/python", "/usr/local/bin/python"}, DepExecutablePath: "/usr/local/bin/pip"},
|
||||
{Name: "Node.js", ExecutableName: "node", ExecutablePaths: []string{"/usr/bin/node", "/usr/local/bin/node"}, DepExecutablePath: "/usr/local/bin/npm"},
|
||||
//{Name: "Java", ExecutableName: "java", ExecutablePaths: []string{"/usr/bin/java", "/usr/local/bin/java"}},
|
||||
}
|
||||
list := getLangList()
|
||||
for i, lang := range list {
|
||||
list[i].Installed = IsInstalledLang(nodeId, lang)
|
||||
status, _ := GetLangInstallStatus(nodeId, lang)
|
||||
list[i].InstallStatus = status
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
// 获取语言列表
|
||||
func GetLangListPlain() []entity.Lang {
|
||||
list := getLangList()
|
||||
return list
|
||||
}
|
||||
|
||||
// 根据语言名获取语言实例
|
||||
func GetLangFromLangName(nodeId string, name string) entity.Lang {
|
||||
langList := GetLangList(nodeId)
|
||||
@@ -86,6 +115,63 @@ func GetLangFromLangName(nodeId string, name string) entity.Lang {
|
||||
return entity.Lang{}
|
||||
}
|
||||
|
||||
// 根据语言名获取语言实例,不包含状态
|
||||
func GetLangFromLangNamePlain(name string) entity.Lang {
|
||||
langList := GetLangListPlain()
|
||||
for _, lang := range langList {
|
||||
if lang.ExecutableName == name {
|
||||
return lang
|
||||
}
|
||||
}
|
||||
return entity.Lang{}
|
||||
}
|
||||
|
||||
func GetLangInstallStatus(nodeId string, lang entity.Lang) (string, error) {
|
||||
if IsMasterNode(nodeId) {
|
||||
lang := GetLangLocal(lang)
|
||||
return lang.InstallStatus, nil
|
||||
} else {
|
||||
lang, err := GetLangRemote(nodeId, lang)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return lang.InstallStatus, nil
|
||||
}
|
||||
}
|
||||
|
||||
func GetLangLocal(lang entity.Lang) entity.Lang{
|
||||
// 检查是否存在执行路径
|
||||
for _, p := range lang.ExecutablePaths {
|
||||
if utils.Exists(p) {
|
||||
lang.InstallStatus = constants.InstallStatusInstalled
|
||||
return lang
|
||||
}
|
||||
}
|
||||
|
||||
// 检查是否正在安装
|
||||
if utils.Exists(lang.LockPath) {
|
||||
lang.InstallStatus = constants.InstallStatusInstalling
|
||||
return lang
|
||||
}
|
||||
|
||||
// 检查其他语言是否在安装
|
||||
if utils.Exists("/tmp/install.lock") {
|
||||
lang.InstallStatus = constants.InstallStatusInstallingOther
|
||||
return lang
|
||||
}
|
||||
|
||||
lang.InstallStatus = constants.InstallStatusNotInstalled
|
||||
return lang
|
||||
}
|
||||
|
||||
func GetLangRemote(nodeId string, lang entity.Lang) (entity.Lang, error) {
|
||||
l, err := RpcClientGetLang(nodeId, lang.Name)
|
||||
if err != nil {
|
||||
return l, err
|
||||
}
|
||||
return l, nil
|
||||
}
|
||||
|
||||
// 是否已安装该依赖
|
||||
func IsInstalledLang(nodeId string, lang entity.Lang) bool {
|
||||
sysInfo, err := GetSystemInfo(nodeId)
|
||||
|
||||
@@ -38,65 +38,98 @@
|
||||
<el-tabs v-model="activeTab" @tab-click="onTabChange">
|
||||
<el-tab-pane v-for="lang in langList" :key="lang.name" :label="lang.name" :name="lang.executable_name"/>
|
||||
</el-tabs>
|
||||
<template v-if="activeLang.installed">
|
||||
<el-table
|
||||
height="calc(100vh - 280px)"
|
||||
:data="computedDepList"
|
||||
:empty-text="depName ? $t('No Data') : $t('Please search dependencies')"
|
||||
v-loading="loading"
|
||||
border
|
||||
>
|
||||
<el-table-column
|
||||
:label="$t('Name')"
|
||||
prop="name"
|
||||
width="180"
|
||||
/>
|
||||
<el-table-column
|
||||
:label="!isShowInstalled ? $t('Latest Version') : $t('Version')"
|
||||
prop="version"
|
||||
width="100"
|
||||
/>
|
||||
<el-table-column
|
||||
v-if="!isShowInstalled"
|
||||
:label="$t('Description')"
|
||||
prop="description"
|
||||
/>
|
||||
<el-table-column
|
||||
:label="$t('Action')"
|
||||
<template v-if="activeLang.install_status === 'installed'">
|
||||
<template v-if="activeLang.executable_name === 'java'">
|
||||
<div class="install-wrapper">
|
||||
<el-button
|
||||
icon="el-icon-check"
|
||||
disabled
|
||||
type="success"
|
||||
>
|
||||
{{$t('Installed')}}
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-table
|
||||
height="calc(100vh - 280px)"
|
||||
:data="computedDepList"
|
||||
:empty-text="depName ? $t('No Data') : $t('Please search dependencies')"
|
||||
v-loading="loading"
|
||||
border
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
v-if="!scope.row.installed"
|
||||
v-loading="getDepLoading(scope.row)"
|
||||
:disabled="getDepLoading(scope.row)"
|
||||
size="mini"
|
||||
type="primary"
|
||||
@click="onClickInstallDep(scope.row)"
|
||||
>
|
||||
{{$t('Install')}}
|
||||
</el-button>
|
||||
<el-button
|
||||
v-else
|
||||
v-loading="getDepLoading(scope.row)"
|
||||
:disabled="getDepLoading(scope.row)"
|
||||
size="mini"
|
||||
type="danger"
|
||||
@click="onClickUninstallDep(scope.row)"
|
||||
>
|
||||
{{$t('Uninstall')}}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-table-column
|
||||
:label="$t('Name')"
|
||||
prop="name"
|
||||
width="180"
|
||||
/>
|
||||
<el-table-column
|
||||
:label="!isShowInstalled ? $t('Latest Version') : $t('Version')"
|
||||
prop="version"
|
||||
width="100"
|
||||
/>
|
||||
<el-table-column
|
||||
v-if="!isShowInstalled"
|
||||
:label="$t('Description')"
|
||||
prop="description"
|
||||
/>
|
||||
<el-table-column
|
||||
:label="$t('Action')"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
v-if="!scope.row.installed"
|
||||
v-loading="getDepLoading(scope.row)"
|
||||
:disabled="getDepLoading(scope.row)"
|
||||
size="mini"
|
||||
type="primary"
|
||||
@click="onClickInstallDep(scope.row)"
|
||||
>
|
||||
{{$t('Install')}}
|
||||
</el-button>
|
||||
<el-button
|
||||
v-else
|
||||
v-loading="getDepLoading(scope.row)"
|
||||
:disabled="getDepLoading(scope.row)"
|
||||
size="mini"
|
||||
type="danger"
|
||||
@click="onClickUninstallDep(scope.row)"
|
||||
>
|
||||
{{$t('Uninstall')}}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</template>
|
||||
</template>
|
||||
<template v-else>
|
||||
<template v-else-if="activeLang.install_status === 'installing'">
|
||||
<div class="install-wrapper">
|
||||
<h3>{{activeLang.name + $t(' is not installed, do you want to install it?')}}</h3>
|
||||
<el-button
|
||||
v-loading="isLoadingInstallLang"
|
||||
:disabled="isLoadingInstallLang"
|
||||
icon="el-icon-loading"
|
||||
disabled
|
||||
type="warning"
|
||||
>
|
||||
{{$t('Installing')}}
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="activeLang.install_status === 'installing-other'">
|
||||
<div class="install-wrapper">
|
||||
<el-button
|
||||
loading="el-icon-close"
|
||||
disabled
|
||||
type="warning"
|
||||
>
|
||||
{{$t('Other language installing')}}
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="activeLang.install_status === 'not-installed'">
|
||||
<div class="install-wrapper">
|
||||
<h4>{{$t('This language is not installed yet.')}}</h4>
|
||||
<el-button
|
||||
icon="el-icon-check"
|
||||
type="primary"
|
||||
style="width: 240px;font-weight: bolder;font-size: 18px"
|
||||
@click="onClickInstallLang"
|
||||
>
|
||||
{{$t('Install')}}
|
||||
@@ -175,6 +208,9 @@ export default {
|
||||
}
|
||||
},
|
||||
async getInstalledDepList () {
|
||||
if (this.activeLang.install_status !== 'installed') return
|
||||
if (this.activeLang.name === 'Java') return
|
||||
|
||||
this.loading = true
|
||||
this.installedDepList = []
|
||||
const res = await this.$request.get(`/nodes/${this.nodeForm._id}/deps/installed`, {
|
||||
@@ -295,19 +331,17 @@ export default {
|
||||
const res = await this.$request.get(`/nodes/${id}/langs`)
|
||||
this.langList = res.data.data
|
||||
this.activeTab = this.langList[0].executable_name || ''
|
||||
await this.getInstalledDepList()
|
||||
setTimeout(() => {
|
||||
this.getInstalledDepList()
|
||||
}, 100)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.node-installation >>> .el-button .el-loading-spinner {
|
||||
margin-top: -13px;
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
.node-installation >>> .el-button .el-loading-spinner .circular {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
.node-installation >>> .install-wrapper .el-button {
|
||||
min-width: 240px;
|
||||
font-weight: bolder;
|
||||
font-size: 18px
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -346,6 +346,10 @@ export default {
|
||||
'Executables': '执行文件',
|
||||
'Latest Version': '最新版本',
|
||||
'Version': '版本',
|
||||
'Installed': '已安装',
|
||||
'Installing': '正在安装',
|
||||
'Other language installing': '其他语言正在安装',
|
||||
'This language is not installed yet.': '语言还未安装',
|
||||
|
||||
// 弹出框
|
||||
'Notification': '提示',
|
||||
|
||||
Reference in New Issue
Block a user