fix SSH 命令脚本、服务端脚本、插件端脚本执行参数优化

This commit is contained in:
bwcx_jzy 2023-03-13 18:21:38 +08:00
parent 51ed92c63d
commit f910fb0a45
No known key found for this signature in database
GPG Key ID: 5E48E9372088B9E5
21 changed files with 395 additions and 136 deletions

View File

@ -11,6 +11,16 @@
3. 【server】修复 节点分发 webhook 输入框的错别字(感谢 @大灰灰
4. 【server】修复 工作空间环境变量操作日志记录错误问题
5. 【all】更新 fastjson2 版本
6. 【all】优化 SSH 命令脚本、服务端脚本、插件端脚本执行参数优化
(感谢 [@大灰灰大](https://gitee.com/linjianhui) [Gitee issues I6IPDY](https://gitee.com/dromara/Jpom/issues/I6IPDY)
### ❌ 不兼容功能
1. 【server】删除 COMMAND_INFO 表 type 字段
### ⚠️ 注意
SSH 命令脚本、服务端脚本、插件端脚本默认参数规则变化:参数描述将必填,默认参数在手动执行时无法删除并且可以查看对应参数描述
------

View File

@ -25,6 +25,7 @@ package io.jpom.model.data;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import io.jpom.JpomApplication;
import io.jpom.script.CommandParam;
import lombok.Data;
import lombok.EqualsAndHashCode;
@ -69,6 +70,10 @@ public class NodeScriptModel extends BaseWorkspaceModel {
return StrUtil.emptyToDefault(lastRunUser, StrUtil.DASHED);
}
public void setDefArgs(String defArgs) {
this.defArgs = CommandParam.convertToParam(defArgs);
}
public File scriptPath() {
return scriptPath(getId());
}

View File

@ -81,7 +81,7 @@ public class NodeScriptProcessBuilder extends BaseRunScript implements Runnable
//
String script = FileUtil.getAbsolutePath(scriptFile);
processBuilder = new ProcessBuilder();
List<String> command = StrUtil.splitTrim(args, StrUtil.SPACE);
List<String> command = CommandParam.toCommandList(args);
command.add(0, script);
CommandUtil.paddingPrefix(command);
log.debug(CollUtil.join(command, StrUtil.SPACE));

View File

@ -0,0 +1,125 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Code Technology Studio
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package io.jpom.script;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.JSONValidator;
import io.jpom.model.BaseJsonModel;
import io.jpom.util.StringUtil;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.*;
import java.util.stream.Collectors;
/**
* 脚本参数
*
* @author bwcx_jzy
* @since 2023/3/13
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class CommandParam extends BaseJsonModel {
/**
* 参数值
*/
private String value;
/**
* 描述
*/
private String desc;
public static String convertToParam(String defArgs) {
JSONValidator.Type type = StringUtil.validatorJson(defArgs);
if (type == null || type == JSONValidator.Type.Value) {
// 旧版本的数据
List<CommandParam> commandParams = CommandParam.convertLineStr(defArgs);
return commandParams == null ? null : JSONObject.toJSONString(commandParams);
} else if (type == JSONValidator.Type.Object) {
return defArgs;
} else {
return defArgs;
}
}
public static String toCommandLine(String params) {
JSONValidator.Type type = StringUtil.validatorJson(params);
if (type == null || type == JSONValidator.Type.Value) {
// 兼容旧数据
return params;
}
List<CommandParam> paramList = params(params);
return Optional.ofNullable(paramList)
.map(commandParams -> commandParams.stream()
.map(CommandParam::getValue)
.collect(Collectors.joining(StrUtil.SPACE)))
.orElse(StrUtil.EMPTY);
}
public static List<String> toCommandList(String params) {
JSONValidator.Type type = StringUtil.validatorJson(params);
if (type == null || type == JSONValidator.Type.Value) {
// 兼容旧数据
return StrUtil.splitTrim(params, StrUtil.SPACE);
}
List<CommandParam> paramList = params(params);
return Optional.ofNullable(paramList)
.map(commandParams -> commandParams.stream()
.map(CommandParam::getValue)
.filter(Objects::nonNull)
.collect(Collectors.toList()))
.orElse(Collections.emptyList());
}
public static List<CommandParam> params(String defParams) {
return StringUtil.jsonConvertArray(defParams, CommandParam.class);
}
public static String checkStr(String str) {
return Opt.ofBlankAble(str)
.map(s -> {
List<CommandParam> params = params(s);
return JSONObject.toJSONString(params);
}).orElse(StrUtil.EMPTY);
}
public static List<CommandParam> convertLineStr(String defArgs) {
List<String> list = StrUtil.splitTrim(defArgs, StrUtil.SPACE);
return Optional.ofNullable(list)
.map(strings -> {
List<CommandParam> commandParams1 = new ArrayList<>(strings.size());
for (int i = 0; i < strings.size(); i++) {
CommandParam commandParam = new CommandParam();
commandParam.setValue(strings.get(i));
commandParam.setDesc("参数" + (i + 1));
commandParams1.add(commandParam);
}
return commandParams1;
})
.orElse(null);
}
}

View File

@ -30,7 +30,6 @@ import io.jpom.common.*;
import io.jpom.common.forward.NodeForward;
import io.jpom.common.forward.NodeUrl;
import io.jpom.common.validator.ValidatorItem;
import top.jpom.model.PageResultDto;
import io.jpom.model.data.NodeModel;
import io.jpom.model.script.ScriptModel;
import io.jpom.model.user.UserModel;
@ -38,6 +37,7 @@ import io.jpom.permission.ClassFeature;
import io.jpom.permission.Feature;
import io.jpom.permission.MethodFeature;
import io.jpom.permission.SystemPermission;
import io.jpom.script.CommandParam;
import io.jpom.service.node.script.NodeScriptServer;
import io.jpom.service.script.ScriptExecuteLogServer;
import io.jpom.service.script.ScriptServer;
@ -48,6 +48,7 @@ import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import top.jpom.model.PageResultDto;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
@ -118,7 +119,7 @@ public class ScriptController extends BaseServerController {
scriptModel.setName(name);
scriptModel.setNodeIds(nodeIds);
scriptModel.setDescription(description);
scriptModel.setDefArgs(defArgs);
scriptModel.setDefArgs(CommandParam.checkStr(defArgs));
Assert.hasText(scriptModel.getContext(), "内容为空");
//

View File

@ -35,6 +35,7 @@ import io.jpom.permission.ClassFeature;
import io.jpom.permission.Feature;
import io.jpom.permission.MethodFeature;
import io.jpom.permission.SystemPermission;
import io.jpom.script.CommandParam;
import io.jpom.service.node.command.CommandExecLogService;
import io.jpom.service.node.command.CommandService;
import io.jpom.service.user.TriggerTokenLogServer;
@ -48,7 +49,6 @@ import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
@ -104,7 +104,7 @@ public class CommandInfoController extends BaseServerController {
*/
@RequestMapping(value = "edit", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
@Feature(method = MethodFeature.EDIT)
public JsonMessage<Object> edit(@RequestBody JSONObject data) {
public JsonMessage<Object> edit(@RequestBody JSONObject data, HttpServletRequest request) {
String name = data.getString("name");
String command = data.getString("command");
String desc = data.getString("desc");
@ -121,22 +121,13 @@ public class CommandInfoController extends BaseServerController {
commandModel.setSshIds(data.getString("sshIds"));
commandModel.setAutoExecCron(autoExecCron);
//
if (StrUtil.isNotEmpty(defParams)) {
List<CommandModel.CommandParam> params = CommandModel.params(defParams);
if (params == null) {
commandModel.setDefParams(StrUtil.EMPTY);
} else {
commandModel.setDefParams(JSONObject.toJSONString(params));
}
} else {
commandModel.setDefParams(StrUtil.EMPTY);
}
commandModel.setDefParams(CommandParam.checkStr(defParams));
if (StrUtil.isEmpty(id)) {
commandService.insert(commandModel);
} else {
commandModel.setId(id);
commandService.updateById(commandModel, getRequest());
commandService.updateById(commandModel, request);
}
return JsonMessage.success("操作成功");
}

View File

@ -22,15 +22,11 @@
*/
package io.jpom.model.data;
import io.jpom.model.BaseJsonModel;
import io.jpom.model.BaseWorkspaceModel;
import io.jpom.util.StringUtil;
import lombok.Data;
import lombok.EqualsAndHashCode;
import top.jpom.h2db.TableName;
import java.util.List;
/**
* 指令信息
*
@ -53,10 +49,6 @@ public class CommandModel extends BaseWorkspaceModel {
* 指令内容
*/
private String command;
/**
* 命令类型0-shell1-powershell
*/
private Integer type;
/**
* 命令默认参数
*/
@ -73,21 +65,4 @@ public class CommandModel extends BaseWorkspaceModel {
* 触发器 token
*/
private String triggerToken;
public List<CommandParam> params() {
return params(getDefParams());
}
public static List<CommandParam> params(String defParams) {
return StringUtil.jsonConvertArray(defParams, CommandParam.class);
}
@Data
@EqualsAndHashCode(callSuper = true)
public static class CommandParam extends BaseJsonModel {
/**
* 参数值
*/
private String value;
}
}

View File

@ -23,6 +23,7 @@
package io.jpom.model.node;
import io.jpom.model.BaseNodeModel;
import io.jpom.script.CommandParam;
import lombok.Data;
import lombok.EqualsAndHashCode;
import top.jpom.h2db.TableName;
@ -79,4 +80,8 @@ public class ScriptCacheModel extends BaseNodeModel {
public void dataId(String id) {
setScriptId(id);
}
public void setDefArgs(String defArgs) {
this.defArgs = CommandParam.convertToParam(defArgs);
}
}

View File

@ -28,6 +28,7 @@ import cn.hutool.core.util.StrUtil;
import io.jpom.JpomApplication;
import io.jpom.common.Const;
import io.jpom.model.BaseWorkspaceModel;
import io.jpom.script.CommandParam;
import io.jpom.system.ExtConfigBean;
import io.jpom.util.CommandUtil;
import io.jpom.util.FileUtils;
@ -76,6 +77,9 @@ public class ScriptModel extends BaseWorkspaceModel {
*/
private String triggerToken;
public void setDefArgs(String defArgs) {
this.defArgs = CommandParam.convertToParam(defArgs);
}
public File scriptPath() {
return scriptPath(getId());

View File

@ -35,7 +35,6 @@ import cn.hutool.db.Entity;
import cn.hutool.extra.ssh.ChannelType;
import cn.hutool.extra.ssh.JschUtil;
import cn.hutool.system.SystemUtil;
import com.alibaba.fastjson2.JSONObject;
import com.jcraft.jsch.ChannelExec;
import io.jpom.common.BaseServerController;
import io.jpom.cron.CronUtils;
@ -46,6 +45,7 @@ import io.jpom.model.data.CommandExecLogModel;
import io.jpom.model.data.CommandModel;
import io.jpom.model.data.SshModel;
import io.jpom.model.user.UserModel;
import io.jpom.script.CommandParam;
import io.jpom.service.ITriggerToken;
import io.jpom.service.h2db.BaseWorkspaceService;
import io.jpom.service.node.ssh.SshService;
@ -63,7 +63,6 @@ import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 命令管理
@ -199,12 +198,11 @@ public class CommandService extends BaseWorkspaceService<CommandModel> implement
*/
public String executeBatch(CommandModel commandModel, String params, String nodes, int triggerExecType) {
Assert.notNull(commandModel, "没有对应对命令");
List<CommandModel.CommandParam> commandParams = CommandModel.params(params);
List<String> sshIds = StrUtil.split(nodes, StrUtil.COMMA, true, true);
Assert.notEmpty(sshIds, "请选择 ssh 节点");
String batchId = IdUtil.fastSimpleUUID();
for (String sshId : sshIds) {
this.executeItem(commandModel, commandParams, sshId, batchId, triggerExecType);
this.executeItem(commandModel, params, sshId, batchId, triggerExecType);
}
return batchId;
}
@ -217,7 +215,7 @@ public class CommandService extends BaseWorkspaceService<CommandModel> implement
* @param sshId ssh id
* @param batchId 批次ID
*/
private void executeItem(CommandModel commandModel, List<CommandModel.CommandParam> commandParams, String sshId, String batchId, int triggerExecType) {
private void executeItem(CommandModel commandModel, String commandParams, String sshId, String batchId, int triggerExecType) {
SshModel sshModel = sshService.getByKey(sshId, false);
CommandExecLogModel commandExecLogModel = new CommandExecLogModel();
@ -232,13 +230,7 @@ public class CommandService extends BaseWorkspaceService<CommandModel> implement
}
commandExecLogModel.setStatus(CommandExecLogModel.Status.ING.getCode());
// 拼接参数
String commandParamsLine;
if (commandParams != null) {
commandExecLogModel.setParams(JSONObject.toJSONString(commandParams));
commandParamsLine = commandParams.stream().map(CommandModel.CommandParam::getValue).collect(Collectors.joining(StrUtil.SPACE));
} else {
commandParamsLine = StrUtil.EMPTY;
}
String commandParamsLine = CommandParam.toCommandLine(commandParams);
commandExecLogService.insert(commandExecLogModel);
ThreadUtil.execute(() -> {
@ -372,7 +364,6 @@ public class CommandService extends BaseWorkspaceService<CommandModel> implement
update.setId(exits.getId());
update.setCommand(data.getCommand());
update.setDesc(data.getDesc());
update.setType(data.getType());
update.setDefParams(data.getDefParams());
update.setAutoExecCron(data.getAutoExecCron());
super.updateById(update);

View File

@ -36,6 +36,7 @@ import io.jpom.common.JsonMessage;
import io.jpom.model.EnvironmentMapBuilder;
import io.jpom.model.script.ScriptModel;
import io.jpom.script.BaseRunScript;
import io.jpom.script.CommandParam;
import io.jpom.service.system.WorkspaceEnvVarService;
import io.jpom.system.ExtConfigBean;
import io.jpom.util.CommandUtil;
@ -79,7 +80,7 @@ public class ServerScriptProcessBuilder extends BaseRunScript implements Runnabl
//
String script = FileUtil.getAbsolutePath(scriptFile);
processBuilder = new ProcessBuilder();
List<String> command = StrUtil.splitTrim(args, StrUtil.SPACE);
List<String> command = CommandParam.toCommandList(args);
command.add(0, script);
CommandUtil.paddingPrefix(command);
log.debug(CollUtil.join(command, StrUtil.SPACE));

View File

@ -27,6 +27,7 @@ import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.fastjson2.JSONObject;
import io.jpom.common.BaseServerController;
import io.jpom.common.Const;
import io.jpom.common.JsonMessage;
import io.jpom.model.script.ScriptExecuteLogModel;
import io.jpom.model.script.ScriptModel;
import io.jpom.model.user.UserModel;
@ -88,7 +89,10 @@ public class ServerScriptHandler extends BaseProxyHandler {
json.put(Const.SOCKET_MSG_TAG, Const.SOCKET_MSG_TAG);
json.put("executeId", executeId);
ServerScriptProcessBuilder.addWatcher(scriptModel, executeId, args, session);
this.sendMsg(session, json.toString());
JsonMessage<String> jsonMessage = new JsonMessage<>(200, "开始执行");
JSONObject jsonObject = jsonMessage.toJson();
jsonObject.putAll(json);
this.sendMsg(session, jsonObject.toString());
break;
}
case stop: {

View File

@ -15,3 +15,4 @@ DROP,DOCKER_INFO,failureMsg
DROP,USEROPERATELOGV1,reqId
ADD,USEROPERATELOGV1,workspaceName,String,50,,工作空间名
ADD,USEROPERATELOGV1,username,String,50,,用户名
DROP,COMMAND_INFO,type,

1 alterType,tableName,name,type,len,defaultValue,comment,notNull
15 DROP,USEROPERATELOGV1,reqId
16 ADD,USEROPERATELOGV1,workspaceName,String,50,,工作空间名
17 ADD,USEROPERATELOGV1,username,String,50,,用户名
18 DROP,COMMAND_INFO,type,

View File

@ -287,7 +287,6 @@ COMMAND_INFO,workspaceId,String,50,,true,false,所属工作空间,
COMMAND_INFO,name,String,100,,false,false,命令名称,
COMMAND_INFO,desc,String,500,,false,false,命令描述,
COMMAND_INFO,command,TEXT,,,false,false,指令内容,
COMMAND_INFO,type,Integer,,0,false,false,命令类型0-shell1-powershell,
COMMAND_INFO,defParams,TEXT,,,false,false,命令参数,
COMMAND_INFO,sshIds,TEXT,,,false,false,绑定的ssh id,
COMMAND_INFO,autoExecCron,String,100,,false,false,自动执行表达式,

1 tableName name type len defaultValue notNull primaryKey comment tableComment
287 COMMAND_INFO name String 100 false false 命令名称
288 COMMAND_INFO desc String 500 false false 命令描述
289 COMMAND_INFO command TEXT false false 指令内容
COMMAND_INFO type Integer 0 false false 命令类型,0-shell,1-powershell
290 COMMAND_INFO defParams TEXT false false 命令参数
291 COMMAND_INFO sshIds TEXT false false 绑定的ssh id
292 COMMAND_INFO autoExecCron String 100 false false 自动执行表达式

View File

@ -16,10 +16,29 @@
</div>
<!--远程下载 -->
<a-modal destroyOnClose v-model="editArgs" title="添加运行参数" @ok="startExecution" @cancel="this.editArgs = false" :maskClosable="false">
<a-form-model :model="temp" :label-col="{ span: 5 }" :wrapper-col="{ span: 24 }" ref="ruleForm">
<a-form-model-item label="执行参数" prop="args">
<a-input v-model="temp.args" placeholder="执行参数,没有参数可以不填写" />
<a-modal destroyOnClose v-model="editArgs" title="添加运行参数" @ok="startExecution" :maskClosable="false">
<a-form-model :model="temp" :label-col="{ span: 4 }" :wrapper-col="{ span: 20 }" ref="ruleForm">
<a-form-model-item label="命令参数" :help="`${commandParams.length ? '所有参数将拼接成字符串以空格分隔形式执行脚本,需要注意参数顺序和未填写值的参数将自动忽略' : ''}`">
<a-row v-for="(item, index) in commandParams" :key="item.key">
<a-col :span="22">
<a-input :addon-before="`参数${index + 1}`" v-model="item.value" :placeholder="`参数值 ${item.desc ? ',' + item.desc : ''}`">
<template slot="suffix">
<a-tooltip v-if="item.desc" :title="item.desc">
<a-icon type="info-circle" style="color: rgba(0, 0, 0, 0.45)" />
</a-tooltip>
</template>
</a-input>
</a-col>
<a-col v-if="!item.desc" :span="2">
<a-row type="flex" justify="center" align="middle">
<a-col>
<a-icon type="minus-circle" @click="() => commandParams.splice(index, 1)" style="color: #ff0000" />
</a-col>
</a-row>
</a-col>
</a-row>
<a-button type="primary" @click="() => commandParams.push({})">添加参数</a-button>
</a-form-model-item>
</a-form-model>
</a-modal>
@ -52,8 +71,9 @@ export default {
scriptStatus: 0,
editArgs: false,
temp: {
args: "",
// args: "",
},
commandParams: [],
//
// logContext: "loading ...",
btnLoading: true,
@ -67,7 +87,11 @@ export default {
},
mounted() {
this.initWebSocket();
this.temp.args = this.defArgs;
if (typeof this.defArgs === "string" && this.defArgs) {
this.commandParams = JSON.parse(this.defArgs);
} else {
this.commandParams = [];
}
},
beforeDestroy() {
if (this.socket) {
@ -145,7 +169,7 @@ export default {
const data = {
op: op,
scriptId: this.scriptId,
args: this.temp.args,
args: JSON.stringify(this.commandParams),
executeId: this.temp.executeId,
};
this.socket.send(JSON.stringify(data));

View File

@ -53,8 +53,28 @@
<code-editor v-model="temp.context" :options="{ mode: 'shell', tabSize: 2, theme: 'abcdef' }"></code-editor>
</div>
</a-form-model-item>
<a-form-model-item label="默认参数" prop="defArgs">
<!-- <a-form-model-item label="默认参数" prop="defArgs">
<a-input v-model="temp.defArgs" placeholder="默认参数" />
</a-form-model-item> -->
<a-form-model-item label="默认参数">
<div v-for="(item, index) in commandParams" :key="item.key">
<a-row type="flex" justify="center" align="middle">
<a-col :span="22">
<a-input :addon-before="`参数${index + 1}描述`" v-model="item.desc" placeholder="参数描述,参数描述没有实际作用,仅是用于提示参数的含义" />
<a-input :addon-before="`参数${index + 1}`" v-model="item.value" placeholder="参数值,添加默认参数后在手动执行脚本时需要填写参数值" />
</a-col>
<a-col :span="2">
<a-row type="flex" justify="center" align="middle">
<a-col>
<a-icon @click="() => commandParams.splice(index, 1)" type="minus-circle" style="color: #ff0000" />
</a-col>
</a-row>
</a-col>
</a-row>
<a-divider style="margin: 5px 0" />
</div>
<a-button type="primary" @click="() => commandParams.push({})">添加参数</a-button>
</a-form-model-item>
<a-form-model-item label="定时执行" prop="autoExecCron">
<a-auto-complete v-model="temp.autoExecCron" placeholder="如果需要定时自动执行则填写,cron 表达式.默认未开启秒级别,需要去修改配置文件中:[system.timerMatchSecond]" option-label-prop="value">
@ -118,6 +138,7 @@ export default {
name: [{ required: true, message: "Please input Script name", trigger: "blur" }],
context: [{ required: true, message: "Please input Script context", trigger: "blur" }],
},
commandParams: [],
};
},
computed: {
@ -144,14 +165,13 @@ export default {
this.loading = false;
});
},
parseTime(v) {
return parseTime(v);
},
parseTime,
//
handleAdd() {
this.temp = {
type: "add",
};
this.commandParams = [];
this.editScriptVisible = true;
},
//
@ -161,6 +181,7 @@ export default {
nodeId: this.node.id,
}).then((res) => {
this.temp = Object.assign({}, res.data);
this.commandParams = this.temp.defArgs ? JSON.parse(this.temp.defArgs) : [];
//
this.editScriptVisible = true;
});
@ -173,6 +194,19 @@ export default {
});
return;
}
if (this.commandParams && this.commandParams.length > 0) {
for (let i = 0; i < this.commandParams.length; i++) {
if (!this.commandParams[i].desc) {
this.$notification.error({
message: "请填写第" + (i + 1) + "个参数的描述",
});
return false;
}
}
this.temp.defArgs = JSON.stringify(this.commandParams);
} else {
this.temp.defArgs = "";
}
//
this.$refs["editScriptForm"].validate((valid) => {
if (!valid) {

View File

@ -83,8 +83,28 @@
<code-editor v-model="temp.context" :options="{ mode: 'shell', tabSize: 2, theme: 'abcdef' }"></code-editor>
</div>
</a-form-model-item>
<a-form-model-item label="默认参数" prop="defArgs">
<!-- <a-form-model-item label="默认参数" prop="defArgs">
<a-input v-model="temp.defArgs" placeholder="默认参数" />
</a-form-model-item> -->
<a-form-model-item label="默认参数">
<div v-for="(item, index) in commandParams" :key="item.key">
<a-row type="flex" justify="center" align="middle">
<a-col :span="22">
<a-input :addon-before="`参数${index + 1}描述`" v-model="item.desc" placeholder="参数描述,参数描述没有实际作用,仅是用于提示参数的含义" />
<a-input :addon-before="`参数${index + 1}`" v-model="item.value" placeholder="参数值,添加默认参数后在手动执行脚本时需要填写参数值" />
</a-col>
<a-col :span="2">
<a-row type="flex" justify="center" align="middle">
<a-col>
<a-icon @click="() => commandParams.splice(index, 1)" type="minus-circle" style="color: #ff0000" />
</a-col>
</a-row>
</a-col>
</a-row>
<a-divider style="margin: 5px 0" />
</div>
<a-button type="primary" @click="() => commandParams.push({})">添加参数</a-button>
</a-form-model-item>
<a-form-model-item label="定时执行" prop="autoExecCron">
<a-auto-complete v-model="temp.autoExecCron" placeholder="如果需要定时自动执行则填写,cron 表达式.默认未开启秒级别,需要去修改配置文件中:[system.timerMatchSecond]" option-label-prop="value">
@ -231,6 +251,7 @@ export default {
context: [{ required: true, message: "Please input Script context", trigger: "blur" }],
},
triggerVisible: false,
commandParams: [],
};
},
computed: {
@ -263,9 +284,7 @@ export default {
this.loading = false;
});
},
parseTime(v) {
return parseTime(v);
},
parseTime,
//
handleEdit(record) {
itemScript({
@ -275,6 +294,7 @@ export default {
if (res.code === 200) {
this.temp = Object.assign({}, res.data);
this.temp.nodeId = record.nodeId;
this.commandParams = this.temp.defArgs ? JSON.parse(this.temp.defArgs) : [];
//
this.editScriptVisible = true;
}
@ -293,6 +313,19 @@ export default {
if (!valid) {
return false;
}
if (this.commandParams && this.commandParams.length > 0) {
for (let i = 0; i < this.commandParams.length; i++) {
if (!this.commandParams[i].desc) {
this.$notification.error({
message: "请填写第" + (i + 1) + "个参数的描述",
});
return false;
}
}
this.temp.defArgs = JSON.stringify(this.commandParams);
} else {
this.temp.defArgs = "";
}
//
editScript(this.temp).then((res) => {
if (res.code === 200) {

View File

@ -15,10 +15,32 @@
</div>
<!--远程下载 -->
<a-modal destroyOnClose v-model="editArgs" title="添加运行参数" @ok="startExecution" @cancel="this.editArgs = false" :maskClosable="false">
<a-form-model :model="temp" :label-col="{ span: 5 }" :wrapper-col="{ span: 24 }" ref="ruleForm">
<a-form-model-item label="执行参数" prop="args">
<a-modal destroyOnClose v-model="editArgs" title="添加运行参数" @ok="startExecution" :maskClosable="false">
<a-form-model :model="temp" :label-col="{ span: 4 }" :wrapper-col="{ span: 20 }" ref="ruleForm">
<!-- <a-form-model-item label="执行参数" prop="args">
<a-input v-model="temp.args" placeholder="执行参数,没有参数可以不填写" />
</a-form-model-item> -->
<a-form-model-item label="命令参数" :help="`${commandParams.length ? '所有参数将拼接成字符串以空格分隔形式执行脚本,需要注意参数顺序和未填写值的参数将自动忽略' : ''}`">
<a-row v-for="(item, index) in commandParams" :key="item.key">
<a-col :span="22">
<a-input :addon-before="`参数${index + 1}`" v-model="item.value" :placeholder="`参数值 ${item.desc ? ',' + item.desc : ''}`">
<template slot="suffix">
<a-tooltip v-if="item.desc" :title="item.desc">
<a-icon type="info-circle" style="color: rgba(0, 0, 0, 0.45)" />
</a-tooltip>
</template>
</a-input>
</a-col>
<a-col v-if="!item.desc" :span="2">
<a-row type="flex" justify="center" align="middle">
<a-col>
<a-icon type="minus-circle" @click="() => commandParams.splice(index, 1)" style="color: #ff0000" />
</a-col>
</a-row>
</a-col>
</a-row>
<a-button type="primary" @click="() => commandParams.push({})">添加参数</a-button>
</a-form-model-item>
</a-form-model>
</a-modal>
@ -47,11 +69,12 @@ export default {
scriptStatus: 0,
editArgs: false,
temp: {
args: "",
// args: "",
},
//
// logContext: "loading ...",
btnLoading: true,
commandParams: [],
};
},
computed: {
@ -62,7 +85,11 @@ export default {
},
mounted() {
this.initWebSocket();
this.temp.args = this.defArgs;
if (typeof this.defArgs === "string" && this.defArgs) {
this.commandParams = JSON.parse(this.defArgs);
} else {
this.commandParams = [];
}
},
beforeDestroy() {
if (this.socket) {
@ -139,7 +166,7 @@ export default {
const data = {
op: op,
id: this.id,
args: this.temp.args,
args: JSON.stringify(this.commandParams),
executeId: this.temp.executeId,
};
this.socket.send(JSON.stringify(data));

View File

@ -90,8 +90,28 @@
<code-editor v-model="temp.context" :options="{ mode: 'shell', tabSize: 2, theme: 'abcdef' }"></code-editor>
</div>
</a-form-model-item>
<a-form-model-item label="默认参数" prop="defArgs">
<!-- <a-form-model-item label="默认参数" prop="defArgs">
<a-input v-model="temp.defArgs" placeholder="默认参数" />
</a-form-model-item> -->
<a-form-model-item label="默认参数">
<div v-for="(item, index) in commandParams" :key="item.key">
<a-row type="flex" justify="center" align="middle">
<a-col :span="22">
<a-input :addon-before="`参数${index + 1}描述`" v-model="item.desc" placeholder="参数描述,参数描述没有实际作用,仅是用于提示参数的含义" />
<a-input :addon-before="`参数${index + 1}`" v-model="item.value" placeholder="参数值,添加默认参数后在手动执行脚本时需要填写参数值" />
</a-col>
<a-col :span="2">
<a-row type="flex" justify="center" align="middle">
<a-col>
<a-icon @click="() => commandParams.splice(index, 1)" type="minus-circle" style="color: #ff0000" />
</a-col>
</a-row>
</a-col>
</a-row>
<a-divider style="margin: 5px 0" />
</div>
<a-button type="primary" @click="() => commandParams.push({})">添加参数</a-button>
</a-form-model-item>
<a-form-model-item label="定时执行" prop="autoExecCron">
<a-auto-complete v-model="temp.autoExecCron" placeholder="如果需要定时自动执行则填写,cron 表达式.默认未开启秒级别,需要去修改配置文件中:[system.timerMatchSecond]" option-label-prop="value">
@ -259,6 +279,7 @@ export default {
syncToWorkspaceVisible: false,
workspaceList: [],
triggerVisible: false,
commandParams: [],
};
},
computed: {
@ -304,15 +325,16 @@ export default {
},
createScript() {
this.temp = {};
this.commandParams = [];
this.editScriptVisible = true;
this.getAllNodeList();
},
//
handleEdit(record) {
this.temp = Object.assign({}, record);
// record;
//
// this.temp.;
this.commandParams = record.defArgs ? JSON.parse(record.defArgs) : [];
this.temp = { ...this.temp, chooseNode: record.nodeIds ? record.nodeIds.split(",") : [] };
this.editScriptVisible = true;
this.getAllNodeList();
@ -324,6 +346,19 @@ export default {
if (!valid) {
return false;
}
if (this.commandParams && this.commandParams.length > 0) {
for (let i = 0; i < this.commandParams.length; i++) {
if (!this.commandParams[i].desc) {
this.$notification.error({
message: "请填写第" + (i + 1) + "个参数的描述",
});
return false;
}
}
this.temp.defArgs = JSON.stringify(this.commandParams);
} else {
this.temp.defArgs = "";
}
//
this.temp.nodeIds = this.temp?.chooseNode?.join(",");
editScript(this.temp).then((res) => {

View File

@ -83,16 +83,24 @@
</a-form-model-item>
<a-form-model-item label="默认参数">
<div class="params-item" v-for="(item, index) in commandParams" :key="item.key">
<div class="item-info">
<a-input addon-before="参数值" v-model="item.value" placeholder="参数值" />
</div>
<div class="item-icon" @click="handleDeleteParam(index)">
<a-icon type="minus-circle" style="color: #ff0000" />
</div>
<div v-for="(item, index) in commandParams" :key="item.key">
<a-row type="flex" justify="center" align="middle">
<a-col :span="22">
<a-input :addon-before="`参数${index + 1}描述`" v-model="item.desc" placeholder="参数描述,参数描述没有实际作用,仅是用于提示参数的含义" />
<a-input :addon-before="`参数${index + 1}`" v-model="item.value" placeholder="参数值,添加默认参数后在手动执行脚本时需要填写参数值" />
</a-col>
<a-col :span="2">
<a-row type="flex" justify="center" align="middle">
<a-col>
<a-icon @click="() => commandParams.splice(index, 1)" type="minus-circle" style="color: #ff0000" />
</a-col>
</a-row>
</a-col>
</a-row>
<a-divider style="margin: 5px 0" />
</div>
<a-button type="primary" @click="handleAddParam">添加参数</a-button>
<a-button type="primary" @click="() => commandParams.push({})">添加参数</a-button>
</a-form-model-item>
<a-form-model-item label="自动执行" prop="autoExecCron">
<a-auto-complete v-model="temp.autoExecCron" placeholder="如果需要定时自动执行则填写,cron 表达式.默认未开启秒级别,需要去修改配置文件中:[system.timerMatchSecond]" option-label-prop="value">
@ -126,16 +134,27 @@
</a-select>
</a-form-model-item>
<a-form-model-item label="命令参数">
<div v-for="item in commandParams" :key="item.key">
<div class="item-info">
<a-input addon-before="参数值" v-model="item.value" placeholder="参数值" />
</div>
<div class="item-icon" @click="handleDeleteParam(index)">
<a-icon type="minus-circle" style="color: #ff0000" />
</div>
</div>
<a-button type="primary" @click="handleAddParam">添加参数</a-button>
<a-form-model-item label="命令参数" :help="`${commandParams.length ? '所有参数将拼接成字符串以空格分隔形式执行脚本,需要注意参数顺序和未填写值的参数将自动忽略' : ''}`">
<a-row v-for="(item, index) in commandParams" :key="item.key">
<a-col :span="22">
<a-input :addon-before="`参数${index + 1}`" v-model="item.value" :placeholder="`参数值 ${item.desc ? ',' + item.desc : ''}`">
<template slot="suffix">
<a-tooltip v-if="item.desc" :title="item.desc">
<a-icon type="info-circle" style="color: rgba(0, 0, 0, 0.45)" />
</a-tooltip>
</template>
</a-input>
</a-col>
<a-col v-if="!item.desc" :span="2">
<a-row type="flex" justify="center" align="middle">
<a-col>
<a-icon type="minus-circle" style="color: #ff0000" @click="() => commandParams.splice(index, 1)" />
</a-col>
</a-row>
</a-col>
</a-row>
<a-button type="primary" @click="() => commandParams.push({})">添加参数</a-button>
</a-form-model-item>
</a-form-model>
</a-modal>
@ -327,6 +346,14 @@ export default {
}
this.formLoading = true;
if (this.commandParams && this.commandParams.length > 0) {
for (let i = 0; i < this.commandParams.length; i++) {
if (!this.commandParams[i].desc) {
this.$notification.error({
message: "请填写第" + (i + 1) + "个参数的描述",
});
return false;
}
}
this.temp.defParams = JSON.stringify(this.commandParams);
} else {
this.temp.defParams = "";
@ -423,19 +450,7 @@ export default {
this.sshList = res.data || [];
});
},
//
handleAddParam() {
this.commandParams.push({});
},
//
handleDeleteParam(index) {
this.commandParams.splice(index, 1);
},
handleParamChange(check) {
if (!check) {
this.commandParams = [];
}
},
handleExecuteCommandOk() {
if (!this.chooseSsh || this.chooseSsh.length <= 0) {
this.$notification.error({
@ -542,22 +557,4 @@ export default {
overflow-y: scroll;
max-height: 300px;
}
.params-item {
display: flex;
align-items: center;
border-bottom: 1px #e2e2e2 solid;
padding-bottom: 5px;
}
.item-info {
display: inline-block;
width: 90%;
}
.item-icon {
display: inline-block;
width: 10%;
text-align: center;
}
</style>

View File

@ -113,20 +113,17 @@ export default {
customRender: (text) => {
return parseTime(text);
},
width: 170,
width: "170px",
},
{
title: "修改时间",
dataIndex: "modifyTimeMillis",
customRender: (text) => {
if (!text) {
return "";
}
return parseTime(text);
},
sorter: true,
width: 180,
width: "170px",
},
{ title: "操作", dataIndex: "operation", align: "center", scopedSlots: { customRender: "operation" }, width: "220px" },
],