fix DSL 项目控制台支持快捷编辑节点脚本(查看流程信息)

This commit is contained in:
bwcx_jzy 2023-12-30 21:39:56 +08:00
parent f07c052017
commit 017c00ee65
No known key found for this signature in database
GPG Key ID: E187D6E9DDDE8C53
36 changed files with 734 additions and 748 deletions

View File

@ -15,6 +15,7 @@
6. 【all】优化 授权目录判断逻辑
7. 【agent】取消 插件端授权目录关闭包含判断(`jpom.whitelist.check-starts-with`)
8. 【server】优化 触发器清理优化、删除用户主动删除关联触发器
9. 【server】优化 DSL 项目控制台支持快捷编辑节点脚本(查看流程信息)
### ⚠️ 注意

View File

@ -49,7 +49,7 @@ import org.dromara.jpom.model.data.DslYmlDto;
import org.dromara.jpom.model.data.NodeProjectInfoModel;
import org.dromara.jpom.model.system.NetstatModel;
import org.dromara.jpom.plugin.PluginFactory;
import org.dromara.jpom.script.DslScriptBuilder;
import org.dromara.jpom.service.script.DslScriptServer;
import org.dromara.jpom.socket.AgentFileTailWatcher;
import org.dromara.jpom.socket.ConsoleCommandOp;
import org.dromara.jpom.util.CommandUtil;
@ -89,14 +89,17 @@ public abstract class AbstractProjectCommander implements ProjectCommander {
protected final SystemCommander systemCommander;
protected final ProjectConfig projectConfig;
protected final ProjectLogConfig projectLogConfig;
protected final DslScriptServer dslScriptServer;
public AbstractProjectCommander(Charset fileCharset,
SystemCommander systemCommander,
ProjectConfig projectConfig) {
ProjectConfig projectConfig,
DslScriptServer dslScriptServer) {
this.fileCharset = fileCharset;
this.systemCommander = systemCommander;
this.projectConfig = projectConfig;
this.projectLogConfig = projectConfig.getLog();
this.dslScriptServer = dslScriptServer;
}
@ -108,7 +111,7 @@ public abstract class AbstractProjectCommander implements ProjectCommander {
* @param nodeProjectInfoModel 项目
* @return null 是条件不足
*/
public abstract String buildJavaCommand(NodeProjectInfoModel nodeProjectInfoModel);
public abstract String buildRunCommand(NodeProjectInfoModel nodeProjectInfoModel);
protected String getRunJavaPath(NodeProjectInfoModel nodeProjectInfoModel, boolean w) {
// if (StrUtil.isEmpty(nodeProjectInfoModel.getJdkId())) {
@ -153,7 +156,7 @@ public abstract class AbstractProjectCommander implements ProjectCommander {
this.runDsl(nodeProjectInfoModel, ConsoleCommandOp.start.name(), (baseProcess, action) -> {
String log = nodeProjectInfoModel.getAbsoluteLog();
try {
DslScriptBuilder.run(baseProcess, nodeProjectInfoModel, action, log, sync);
dslScriptServer.run(baseProcess, nodeProjectInfoModel, action, log, sync);
} catch (Exception e) {
throw Lombok.sneakyThrow(e);
}
@ -161,7 +164,7 @@ public abstract class AbstractProjectCommander implements ProjectCommander {
});
} else {
String command = this.buildJavaCommand(nodeProjectInfoModel);
String command = this.buildRunCommand(nodeProjectInfoModel);
if (command == null) {
return CommandOpResult.of(false, "没有需要执行的命令");
}
@ -172,7 +175,7 @@ public abstract class AbstractProjectCommander implements ProjectCommander {
if (SystemUtil.getOsInfo().isWindows()) {
CommandUtil.execSystemCommand(command, file);
} else {
CommandUtil.asyncExeLocalCommand(file, command);
CommandUtil.asyncExeLocalCommand(command, file);
}
} catch (Exception e) {
log.error("执行命令失败", e);
@ -232,7 +235,7 @@ public abstract class AbstractProjectCommander implements ProjectCommander {
this.runDsl(nodeProjectInfoModel, ConsoleCommandOp.stop.name(), (process, action) -> {
String log = nodeProjectInfoModel.getAbsoluteLog();
try {
DslScriptBuilder.run(process, nodeProjectInfoModel, action, log, sync);
dslScriptServer.run(process, nodeProjectInfoModel, action, log, sync);
} catch (Exception e) {
throw Lombok.sneakyThrow(e);
}
@ -305,7 +308,7 @@ public abstract class AbstractProjectCommander implements ProjectCommander {
if (ArrayUtil.contains(other, "fileChange")) {
RunMode runMode = nodeProjectInfoModel.getRunMode();
if (runMode == RunMode.Dsl) {
DslYmlDto dslYmlDto = nodeProjectInfoModel.dslConfig();
DslYmlDto dslYmlDto = nodeProjectInfoModel.mustDslConfig();
if (dslYmlDto.hasRunProcess(ConsoleCommandOp.reload.name())) {
DslYmlDto.Run run = dslYmlDto.getRun();
Boolean fileChangeReload = run.getFileChangeReload();
@ -365,13 +368,14 @@ public abstract class AbstractProjectCommander implements ProjectCommander {
}
this.asyncWebHooks(nodeProjectInfoModel, "beforeRestart");
if (runMode == RunMode.Dsl) {
DslYmlDto.BaseProcess dslProcess = nodeProjectInfoModel.tryDslProcess("restart");
DslYmlDto.BaseProcess dslProcess = nodeProjectInfoModel.tryDslProcess(ConsoleCommandOp.restart.name());
if (dslProcess != null) {
// 如果存在自定义 restart 流程
//
this.runDsl(nodeProjectInfoModel, ConsoleCommandOp.restart.name(), (process, action) -> {
String log = nodeProjectInfoModel.getAbsoluteLog();
try {
DslScriptBuilder.run(process, nodeProjectInfoModel, action, log, false);
dslScriptServer.run(process, nodeProjectInfoModel, action, log, false);
} catch (Exception e) {
throw Lombok.sneakyThrow(e);
}
@ -475,8 +479,7 @@ public abstract class AbstractProjectCommander implements ProjectCommander {
RunMode runMode = nodeProjectInfoModel.getRunMode();
boolean autoBackToFile = projectLogConfig.isAutoBackupToFile();
if (runMode == RunMode.Dsl) {
DslYmlDto dslYmlDto = nodeProjectInfoModel.dslConfig();
return Optional.ofNullable(dslYmlDto)
return Optional.ofNullable(nodeProjectInfoModel.dslConfig())
.map(DslYmlDto::getConfig)
.map(DslYmlDto.Config::getAutoBackToFile)
.orElse(autoBackToFile);
@ -532,7 +535,7 @@ public abstract class AbstractProjectCommander implements ProjectCommander {
List<String> status = this.runDsl(nodeProjectInfoModel, ConsoleCommandOp.status.name(), (baseProcess, action) -> {
// 提前判断脚本 id,避免填写错误在删除项目检测状态时候异常
try {
Tuple tuple = DslScriptBuilder.syncRun(baseProcess, nodeProjectInfoModel, action);
Tuple tuple = dslScriptServer.syncRun(baseProcess, nodeProjectInfoModel, action);
return tuple.get(1);
} catch (IllegalArgument2Exception argument2Exception) {
log.warn("执行 DSL 脚本异常:{}", argument2Exception.getMessage());
@ -576,9 +579,10 @@ public abstract class AbstractProjectCommander implements ProjectCommander {
return this.runDsl(nodeProjectInfoModel, ConsoleCommandOp.reload.name(), (baseProcess, action) -> {
// 提前判断脚本 id,避免填写错误在删除项目检测状态时候异常
try {
Tuple tuple = DslScriptBuilder.syncRun(baseProcess, nodeProjectInfoModel, action);
Tuple tuple = dslScriptServer.syncRun(baseProcess, nodeProjectInfoModel, action);
int code = tuple.get(0);
List<String> list = tuple.get(1);
// 如果退出码为 0 认为执行成功
return CommandOpResult.of(code == 0, list);
} catch (IllegalArgument2Exception argument2Exception) {
log.warn("执行 DSL 脚本异常:{}", argument2Exception.getMessage());
@ -595,12 +599,23 @@ public abstract class AbstractProjectCommander implements ProjectCommander {
*/
protected String status(String tag) {
String jpsStatus = this.getJpsStatus(tag);
if (StrUtil.equals(AbstractProjectCommander.STOP_TAG, jpsStatus) && SystemUtil.getOsInfo().isLinux()) {
return getLinuxPsStatus(tag);
if (StrUtil.equals(AbstractProjectCommander.STOP_TAG, jpsStatus)) {
// 通过系统命令查询
return this.bySystemPs(tag);
}
return jpsStatus;
}
/**
* 通过系统命令查询进程是否存在
*
* @param tag 进程标识
* @return 是否存在
*/
protected String bySystemPs(String tag) {
return AbstractProjectCommander.STOP_TAG;
}
/**
* 尝试jps 中查看进程id
*
@ -615,26 +630,6 @@ public abstract class AbstractProjectCommander implements ProjectCommander {
return StrUtil.format("{}:{}", AbstractProjectCommander.RUNNING_TAG, pid);
}
/**
* 尝试ps -ef | grep 中查看进程id
*
* @param tag 进程标识
* @return 运行标识
*/
private String getLinuxPsStatus(String tag) {
String execSystemCommand = CommandUtil.execSystemCommand("ps -ef | grep " + tag);
log.debug("getLinuxPsStatus {} {}", tag, execSystemCommand);
List<String> list = StrSplitter.splitTrim(execSystemCommand, StrUtil.LF, true);
for (String item : list) {
if (JvmUtil.checkCommandLineIsJpom(item, tag)) {
String[] split = StrUtil.splitToArray(item, StrUtil.SPACE);
return StrUtil.format("{}:{}", AbstractProjectCommander.RUNNING_TAG, split[1]);
}
}
return AbstractProjectCommander.STOP_TAG;
}
//---------------------------------------------------- 基本操作----end
/**

View File

@ -23,11 +23,13 @@
package org.dromara.jpom.common.commander;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.text.StrSplitter;
import cn.hutool.core.util.StrUtil;
import lombok.Lombok;
import lombok.extern.slf4j.Slf4j;
import org.dromara.jpom.configuration.ProjectConfig;
import org.dromara.jpom.configuration.ProjectLogConfig;
import org.dromara.jpom.model.data.NodeProjectInfoModel;
import org.dromara.jpom.service.script.DslScriptServer;
import org.dromara.jpom.util.CommandUtil;
import org.dromara.jpom.util.JvmUtil;
@ -43,16 +45,18 @@ import java.util.Optional;
* @author bwcx_jzy
* @since 2021/12/17
*/
@Slf4j
public abstract class BaseUnixProjectCommander extends AbstractProjectCommander {
public BaseUnixProjectCommander(Charset fileCharset,
SystemCommander systemCommander,
ProjectConfig projectConfig) {
super(fileCharset, systemCommander, projectConfig);
ProjectConfig projectConfig,
DslScriptServer dslScriptServer) {
super(fileCharset, systemCommander, projectConfig, dslScriptServer);
}
@Override
public String buildJavaCommand(NodeProjectInfoModel nodeProjectInfoModel) {
public String buildRunCommand(NodeProjectInfoModel nodeProjectInfoModel) {
String path = NodeProjectInfoModel.getClassPathLib(nodeProjectInfoModel);
if (StrUtil.isBlank(path)) {
return null;
@ -82,7 +86,7 @@ public abstract class BaseUnixProjectCommander extends AbstractProjectCommander
result.add("Kill not completed, test kill -9");
String cmd = String.format("kill -9 %s", pid);
try {
CommandUtil.asyncExeLocalCommand(file, cmd);
CommandUtil.asyncExeLocalCommand(cmd, file);
} catch (Exception e) {
throw Lombok.sneakyThrow(e);
}
@ -97,4 +101,24 @@ public abstract class BaseUnixProjectCommander extends AbstractProjectCommander
return CommandOpResult.of(success, status(tag)).appendMsg(result);
// return status(tag) + StrUtil.SPACE + kill;
}
/**
* 尝试ps -ef | grep 中查看进程id
*
* @param tag 进程标识
* @return 运行标识
*/
@Override
protected String bySystemPs(String tag) {
String execSystemCommand = CommandUtil.execSystemCommand("ps -ef | grep " + tag);
log.debug("getPsStatus {} {}", tag, execSystemCommand);
List<String> list = StrSplitter.splitTrim(execSystemCommand, StrUtil.LF, true);
for (String item : list) {
if (JvmUtil.checkCommandLineIsJpom(item, tag)) {
String[] split = StrUtil.splitToArray(item, StrUtil.SPACE);
return StrUtil.format("{}:{}", AbstractProjectCommander.RUNNING_TAG, split[1]);
}
}
return AbstractProjectCommander.STOP_TAG;
}
}

View File

@ -37,7 +37,7 @@ public interface ProjectCommander {
* @param nodeProjectInfoModel 项目
* @return null 是条件不足
*/
String buildJavaCommand(NodeProjectInfoModel nodeProjectInfoModel);
String buildRunCommand(NodeProjectInfoModel nodeProjectInfoModel);
/**
* 执行 webhooks 通知

View File

@ -25,11 +25,13 @@ package org.dromara.jpom.common.commander.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.StrSplitter;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.dromara.jpom.common.commander.BaseUnixProjectCommander;
import org.dromara.jpom.common.commander.Commander;
import org.dromara.jpom.common.commander.SystemCommander;
import org.dromara.jpom.model.system.NetstatModel;
import org.dromara.jpom.configuration.AgentConfig;
import org.dromara.jpom.model.system.NetstatModel;
import org.dromara.jpom.service.script.DslScriptServer;
import org.dromara.jpom.util.CommandUtil;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Primary;
@ -47,11 +49,13 @@ import java.util.stream.Collectors;
@Conditional(Commander.Linux.class)
@Service
@Primary
@Slf4j
public class LinuxProjectCommander extends BaseUnixProjectCommander {
public LinuxProjectCommander(AgentConfig agentConfig,
SystemCommander systemCommander) {
super(agentConfig.getProject().getLog().getFileCharset(), systemCommander, agentConfig.getProject());
SystemCommander systemCommander,
DslScriptServer dslScriptServer) {
super(agentConfig.getProject().getLog().getFileCharset(), systemCommander, agentConfig.getProject(), dslScriptServer);
}
@Override

View File

@ -30,6 +30,7 @@ import org.dromara.jpom.common.commander.Commander;
import org.dromara.jpom.common.commander.SystemCommander;
import org.dromara.jpom.configuration.AgentConfig;
import org.dromara.jpom.model.system.NetstatModel;
import org.dromara.jpom.service.script.DslScriptServer;
import org.dromara.jpom.util.CommandUtil;
import org.springframework.context.annotation.Conditional;
import org.springframework.stereotype.Service;
@ -50,8 +51,9 @@ import java.util.stream.Collectors;
public class MacOsProjectCommander extends BaseUnixProjectCommander {
public MacOsProjectCommander(AgentConfig agentConfig,
SystemCommander systemCommander) {
super(agentConfig.getProject().getLog().getFileCharset(), systemCommander, agentConfig.getProject());
SystemCommander systemCommander,
DslScriptServer dslScriptServer) {
super(agentConfig.getProject().getLog().getFileCharset(), systemCommander, agentConfig.getProject(), dslScriptServer);
}
@Override
@ -71,26 +73,29 @@ public class MacOsProjectCommander extends BaseUnixProjectCommander {
if (CollUtil.isEmpty(netList)) {
return null;
}
return netList.stream().map(str -> {
List<String> list = StrSplitter.splitTrim(str, " ", true);
if (list.size() < 10) {
return null;
}
NetstatModel netstatModel = new NetstatModel();
netstatModel.setProtocol(list.get(7));
//netstatModel.setReceive(list.get(1));
//netstatModel.setSend(list.get(2));
netstatModel.setLocal(list.get(8));
netstatModel.setForeign(list.get(4));
if ("tcp".equalsIgnoreCase(netstatModel.getProtocol())) {
netstatModel.setStatus(CollUtil.get(list, 9));
netstatModel.setName(CollUtil.get(list, 0));
} else {
netstatModel.setStatus(StrUtil.DASHED);
netstatModel.setName(CollUtil.get(list, 5));
}
return netList.stream()
.map(str -> {
List<String> list = StrSplitter.splitTrim(str, " ", true);
if (list.size() < 10) {
return null;
}
NetstatModel netstatModel = new NetstatModel();
netstatModel.setProtocol(list.get(7));
//netstatModel.setReceive(list.get(1));
//netstatModel.setSend(list.get(2));
netstatModel.setLocal(list.get(8));
netstatModel.setForeign(list.get(4));
if ("tcp".equalsIgnoreCase(netstatModel.getProtocol())) {
netstatModel.setStatus(CollUtil.get(list, 9));
netstatModel.setName(CollUtil.get(list, 0));
} else {
netstatModel.setStatus(StrUtil.DASHED);
netstatModel.setName(CollUtil.get(list, 5));
}
return netstatModel;
}).filter(Objects::nonNull).collect(Collectors.toList());
return netstatModel;
})
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
}

View File

@ -32,6 +32,7 @@ import org.dromara.jpom.common.commander.SystemCommander;
import org.dromara.jpom.model.data.NodeProjectInfoModel;
import org.dromara.jpom.model.system.NetstatModel;
import org.dromara.jpom.configuration.AgentConfig;
import org.dromara.jpom.service.script.DslScriptServer;
import org.dromara.jpom.util.CommandUtil;
import org.dromara.jpom.util.JvmUtil;
import org.springframework.context.annotation.Conditional;
@ -51,12 +52,13 @@ import java.util.Optional;
public class WindowsProjectCommander extends AbstractProjectCommander {
public WindowsProjectCommander(AgentConfig agentConfig,
SystemCommander systemCommander) {
super(agentConfig.getProject().getLog().getFileCharset(), systemCommander, agentConfig.getProject());
SystemCommander systemCommander,
DslScriptServer dslScriptServer) {
super(agentConfig.getProject().getLog().getFileCharset(), systemCommander, agentConfig.getProject(), dslScriptServer);
}
@Override
public String buildJavaCommand(NodeProjectInfoModel nodeProjectInfoModel) {
public String buildRunCommand(NodeProjectInfoModel nodeProjectInfoModel) {
String classPath = NodeProjectInfoModel.getClassPathLib(nodeProjectInfoModel);
if (StrUtil.isBlank(classPath)) {
return null;
@ -122,4 +124,7 @@ public class WindowsProjectCommander extends AbstractProjectCommander {
}
return array;
}
// tasklist | findstr /s /i "java"
// wmic process where caption="javaw.exe" get processid,caption,commandline /value
}

View File

@ -22,21 +22,26 @@
*/
package org.dromara.jpom.controller.manage;
import cn.hutool.core.lang.Tuple;
import cn.keepbx.jpom.IJsonMessage;
import cn.keepbx.jpom.model.JsonMessage;
import com.alibaba.fastjson2.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.dromara.jpom.common.BaseAgentController;
import org.dromara.jpom.common.commander.ProjectCommander;
import org.dromara.jpom.model.RunMode;
import org.dromara.jpom.model.data.DslYmlDto;
import org.dromara.jpom.model.data.NodeProjectInfoModel;
import org.dromara.jpom.service.script.DslScriptServer;
import org.dromara.jpom.socket.ConsoleCommandOp;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/**
* 管理的信息获取接口
@ -50,9 +55,12 @@ import java.util.List;
public class ProjectListController extends BaseAgentController {
private final ProjectCommander projectCommander;
private final DslScriptServer dslScriptServer;
public ProjectListController(ProjectCommander projectCommander) {
public ProjectListController(ProjectCommander projectCommander,
DslScriptServer dslScriptServer) {
this.projectCommander = projectCommander;
this.dslScriptServer = dslScriptServer;
}
/**
@ -69,13 +77,24 @@ public class ProjectListController extends BaseAgentController {
RunMode runMode = nodeProjectInfoModel.getRunMode();
if (runMode != RunMode.Dsl && runMode != RunMode.File) {
// 返回实际执行的命令
String command = projectCommander.buildJavaCommand(nodeProjectInfoModel);
String command = projectCommander.buildRunCommand(nodeProjectInfoModel);
nodeProjectInfoModel.setRunCommand(command);
}
if (runMode == RunMode.Dsl) {
DslYmlDto dslYmlDto = nodeProjectInfoModel.dslConfig();
DslYmlDto dslYmlDto = nodeProjectInfoModel.mustDslConfig();
boolean reload = dslYmlDto.hasRunProcess(ConsoleCommandOp.reload.name());
nodeProjectInfoModel.setCanReload(reload);
// 查询 dsl 流程信息
List<JSONObject> list = Arrays.stream(ConsoleCommandOp.values())
.filter(ConsoleCommandOp::isCanOpt)
.map(consoleCommandOp -> {
Tuple tuple = dslScriptServer.resolveProcessScript(nodeProjectInfoModel, dslYmlDto, consoleCommandOp);
JSONObject jsonObject = tuple.get(0);
jsonObject.put("process", consoleCommandOp);
return jsonObject;
})
.collect(Collectors.toList());
nodeProjectInfoModel.setDslProcessInfo(list);
}
}
return JsonMessage.success("", nodeProjectInfoModel);

View File

@ -20,7 +20,7 @@
* 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 org.dromara.jpom.controller.manage.log;
package org.dromara.jpom.controller.manage;
import cn.hutool.core.io.FileUtil;
import cn.hutool.extra.servlet.ServletUtil;
@ -50,11 +50,11 @@ import java.util.List;
@RestController
@RequestMapping(value = "manage/log")
@Slf4j
public class LogBackController extends BaseAgentController {
public class ProjectLogBackController extends BaseAgentController {
private final ProjectCommander projectCommander;
public LogBackController(ProjectCommander projectCommander) {
public ProjectLogBackController(ProjectCommander projectCommander) {
this.projectCommander = projectCommander;
}

View File

@ -23,12 +23,10 @@
package org.dromara.jpom.model.data;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.setting.yaml.YamlUtil;
import cn.keepbx.jpom.model.BaseJsonModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.util.Assert;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
@ -63,14 +61,6 @@ public class DslYmlDto extends BaseJsonModel {
*/
private Config config;
public DslYmlDto.BaseProcess runProcess(String opt) {
DslYmlDto.Run run = this.getRun();
Assert.notNull(run, "yml 未配置 运行管理");
DslYmlDto.BaseProcess baseProcess = (DslYmlDto.BaseProcess) ReflectUtil.getFieldValue(run, opt);
Assert.notNull(baseProcess, "未找到对应的类型或者未配置 " + opt);
return baseProcess;
}
/**
* 判断是否包含指定流程
*
@ -116,6 +106,8 @@ public class DslYmlDto extends BaseJsonModel {
/**
* 重新加载
*
* @see org.dromara.jpom.socket.ConsoleCommandOp
*/
public static class Reload extends BaseProcess {
@ -123,6 +115,8 @@ public class DslYmlDto extends BaseJsonModel {
/**
* 启动流程
*
* @see org.dromara.jpom.socket.ConsoleCommandOp
*/
public static class Start extends BaseProcess {
@ -130,6 +124,8 @@ public class DslYmlDto extends BaseJsonModel {
/**
* 获取状态流程
*
* @see org.dromara.jpom.socket.ConsoleCommandOp
*/
public static class Status extends BaseProcess {
@ -137,6 +133,8 @@ public class DslYmlDto extends BaseJsonModel {
/**
* 停止流程
*
* @see org.dromara.jpom.socket.ConsoleCommandOp
*/
public static class Stop extends BaseProcess {
@ -144,6 +142,8 @@ public class DslYmlDto extends BaseJsonModel {
/**
* 重启流程
*
* @see org.dromara.jpom.socket.ConsoleCommandOp
*/
public static class Restart extends BaseProcess {
@ -164,15 +164,6 @@ public class DslYmlDto extends BaseJsonModel {
* 执行脚本的环境变量
*/
private Map<String, String> scriptEnv;
/**
* 通过 脚本模版运行
*
* @return true
*/
public boolean runByScript() {
return StrUtil.isNotEmpty(this.getScriptId());
}
}
@Data

View File

@ -27,6 +27,7 @@ import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONObject;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.jpom.model.RunMode;
@ -108,10 +109,16 @@ public class NodeProjectInfoModel extends BaseWorkspaceModel {
* dsl 环境变量
*/
private String dslEnv;
// ---------------- 中转字段 start
/**
* 是否可以重新加载
*/
private Boolean canReload;
/**
* DSL 流程信息统计
*/
private List<JSONObject> dslProcessInfo;
// ---------------- 中转字段 end
public String getJavaExtDirsCp() {
return StrUtil.emptyToDefault(javaExtDirsCp, StrUtil.EMPTY);
@ -263,6 +270,11 @@ public class NodeProjectInfoModel extends BaseWorkspaceModel {
return StrUtil.emptyToDefault(token, StrUtil.EMPTY);
}
/**
* 获取当前 dsl 配置
*
* @return DslYmlDto
*/
public DslYmlDto dslConfig() {
String dslContent = this.getDslContent();
if (StrUtil.isEmpty(dslContent)) {
@ -271,6 +283,17 @@ public class NodeProjectInfoModel extends BaseWorkspaceModel {
return DslYmlDto.build(dslContent);
}
/**
* 必须存在 dsl 配置
*
* @return DslYmlDto
*/
public DslYmlDto mustDslConfig() {
DslYmlDto dslYmlDto = this.dslConfig();
Assert.notNull(dslYmlDto, "未配置 dsl 信息(项目信息错误)");
return dslYmlDto;
}
/**
* 获取 dsl 流程信息
*
@ -279,6 +302,16 @@ public class NodeProjectInfoModel extends BaseWorkspaceModel {
*/
public DslYmlDto.BaseProcess tryDslProcess(String opt) {
DslYmlDto build = dslConfig();
return tryDslProcess(build, opt);
}
/**
* 获取 dsl 流程信息
*
* @param opt 操作
* @return 结果
*/
public static DslYmlDto.BaseProcess tryDslProcess(DslYmlDto build, String opt) {
return Optional.ofNullable(build)
.map(DslYmlDto::getRun)
.map(run -> (DslYmlDto.BaseProcess) ReflectUtil.getFieldValue(run, opt))
@ -292,8 +325,8 @@ public class NodeProjectInfoModel extends BaseWorkspaceModel {
* @return 结果
*/
public DslYmlDto.BaseProcess getDslProcess(String opt) {
DslYmlDto build = dslConfig();
Assert.notNull(build, "yml 还未配置");
return build.runProcess(opt);
DslYmlDto.BaseProcess baseProcess = this.tryDslProcess(opt);
Assert.notNull(baseProcess, "DSL 未配置运行管理或者未配置 " + opt + " 流程");
return baseProcess;
}
}

View File

@ -28,38 +28,18 @@ import cn.hutool.core.date.DateTime;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.LineHandler;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.lang.Tuple;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.net.url.UrlQuery;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.dromara.jpom.JpomApplication;
import org.dromara.jpom.common.Const;
import org.dromara.jpom.common.IllegalArgument2Exception;
import org.dromara.jpom.configuration.ProjectLogConfig;
import org.dromara.jpom.model.EnvironmentMapBuilder;
import org.dromara.jpom.model.data.DslYmlDto;
import org.dromara.jpom.model.data.NodeProjectInfoModel;
import org.dromara.jpom.model.data.NodeScriptModel;
import org.dromara.jpom.service.script.NodeScriptServer;
import org.dromara.jpom.service.system.AgentWorkspaceEnvVarService;
import org.dromara.jpom.configuration.AgentConfig;
import org.dromara.jpom.system.ExtConfigBean;
import org.dromara.jpom.util.CommandUtil;
import org.dromara.jpom.util.FileUtils;
import java.io.File;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Future;
/**
* dsl 执行脚本
@ -78,11 +58,11 @@ public class DslScriptBuilder extends BaseRunScript implements Runnable {
private boolean autoDelete;
private EnvironmentMapBuilder environmentMapBuilder;
private DslScriptBuilder(String action,
EnvironmentMapBuilder environmentMapBuilder,
String args,
String log,
Charset charset) {
public DslScriptBuilder(String action,
EnvironmentMapBuilder environmentMapBuilder,
String args,
String log,
Charset charset) {
super(FileUtil.file(log), charset);
this.action = action;
this.environmentMapBuilder = environmentMapBuilder;
@ -191,102 +171,5 @@ public class DslScriptBuilder extends BaseRunScript implements Runnable {
}
}
/**
* 异步执行
*
* @param scriptProcess 脚本流程
* @param log 日志
*/
public static void run(DslYmlDto.BaseProcess scriptProcess, NodeProjectInfoModel nodeProjectInfoModel, String action, String log, boolean sync) throws Exception {
DslScriptBuilder builder = DslScriptBuilder.create(scriptProcess, nodeProjectInfoModel, action, log);
Future<?> execute = ThreadUtil.execAsync(builder);
if (sync) {
execute.get();
}
}
/**
* 同步执行
*
* @param scriptProcess 脚本流程
*/
public static Tuple syncRun(DslYmlDto.BaseProcess scriptProcess, NodeProjectInfoModel nodeProjectInfoModel, String action) {
try (DslScriptBuilder builder = DslScriptBuilder.create(scriptProcess, nodeProjectInfoModel, action, null)) {
return builder.syncExecute();
}
}
/**
* 构建 DSL 执行器
*
* @param scriptProcess 脚本流程
* @param nodeProjectInfoModel 项目
* @param log 日志路径
* @param action 具体操作
*/
private static DslScriptBuilder create(DslYmlDto.BaseProcess scriptProcess, NodeProjectInfoModel nodeProjectInfoModel, String action, String log) {
NodeScriptServer nodeScriptServer = SpringUtil.getBean(NodeScriptServer.class);
AgentConfig agentConfig = SpringUtil.getBean(AgentConfig.class);
ProjectLogConfig logConfig = agentConfig.getProject().getLog();
String scriptId = scriptProcess.getScriptId();
cn.hutool.core.lang.Assert.notBlank(scriptId, () -> new IllegalArgument2Exception("请填写脚本模板id"));
NodeScriptModel item = nodeScriptServer.getItem(scriptId);
EnvironmentMapBuilder environment = DslScriptBuilder.environment(nodeProjectInfoModel, scriptProcess);
File scriptFile;
boolean autoDelete = false;
if (item == null) {
scriptFile = FileUtil.file(nodeProjectInfoModel.allLib(), scriptId);
cn.hutool.core.lang.Assert.isTrue(FileUtil.isFile(scriptFile), () -> new IllegalArgument2Exception("脚本模版不存在:" + scriptProcess.getScriptId()));
} else {
scriptFile = DslScriptBuilder.initScriptFile(item);
// 系统生成的脚本需要自动删除
autoDelete = true;
}
DslScriptBuilder builder = new DslScriptBuilder(action, environment, scriptProcess.getScriptArgs(), log, logConfig.getFileCharset());
builder.setScriptFile(scriptFile);
builder.setAutoDelete(autoDelete);
return builder;
}
/**
* 创建脚本文件
*
* @param scriptModel 脚本对象
* @return file
*/
private static File initScriptFile(NodeScriptModel scriptModel) {
String dataPath = JpomApplication.getInstance().getDataPath();
File scriptFile = FileUtil.file(dataPath, Const.SCRIPT_RUN_CACHE_DIRECTORY, StrUtil.format("{}.{}", IdUtil.fastSimpleUUID(), CommandUtil.SUFFIX));
// 替换内容
String context = scriptModel.getContext();
FileUtils.writeScript(context, scriptFile, ExtConfigBean.getConsoleLogCharset());
return scriptFile;
}
private static EnvironmentMapBuilder environment(NodeProjectInfoModel nodeProjectInfoModel, DslYmlDto.BaseProcess scriptProcess) {
//
AgentWorkspaceEnvVarService workspaceService = SpringUtil.getBean(AgentWorkspaceEnvVarService.class);
EnvironmentMapBuilder environmentMapBuilder = workspaceService.getEnv(nodeProjectInfoModel.getWorkspaceId());
// 项目配置的环境变量
String dslEnv = nodeProjectInfoModel.getDslEnv();
Opt.ofBlankAble(dslEnv)
.map(s -> UrlQuery.of(s, CharsetUtil.CHARSET_UTF_8))
.map(UrlQuery::getQueryMap)
.map(map -> {
Map<String, String> map1 = MapUtil.newHashMap();
for (Map.Entry<CharSequence, CharSequence> entry : map.entrySet()) {
map1.put(StrUtil.toString(entry.getKey()), StrUtil.toString(entry.getValue()));
}
return map1;
})
.ifPresent(environmentMapBuilder::putStr);
//
environmentMapBuilder
.putStr(scriptProcess.getScriptEnv())
.put("PROJECT_ID", nodeProjectInfoModel.getId())
.put("PROJECT_NAME", nodeProjectInfoModel.getName())
.put("PROJECT_PATH", nodeProjectInfoModel.allLib());
return environmentMapBuilder;
}
}

View File

@ -0,0 +1,207 @@
package org.dromara.jpom.service.script;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.lang.Tuple;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.net.url.UrlQuery;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONObject;
import org.dromara.jpom.JpomApplication;
import org.dromara.jpom.common.Const;
import org.dromara.jpom.common.IllegalArgument2Exception;
import org.dromara.jpom.configuration.ProjectLogConfig;
import org.dromara.jpom.model.EnvironmentMapBuilder;
import org.dromara.jpom.model.data.DslYmlDto;
import org.dromara.jpom.model.data.NodeProjectInfoModel;
import org.dromara.jpom.model.data.NodeScriptModel;
import org.dromara.jpom.script.DslScriptBuilder;
import org.dromara.jpom.service.system.AgentWorkspaceEnvVarService;
import org.dromara.jpom.socket.ConsoleCommandOp;
import org.dromara.jpom.system.ExtConfigBean;
import org.dromara.jpom.util.CommandUtil;
import org.dromara.jpom.util.FileUtils;
import org.springframework.stereotype.Service;
import java.io.File;
import java.util.Map;
import java.util.concurrent.Future;
/**
* @author bwcx_jzy
* @since 23/12/30 030
*/
@Service
public class DslScriptServer {
private final AgentWorkspaceEnvVarService agentWorkspaceEnvVarService;
private final NodeScriptServer nodeScriptServer;
private final ProjectLogConfig logConfig;
private final JpomApplication jpomApplication;
public DslScriptServer(AgentWorkspaceEnvVarService agentWorkspaceEnvVarService,
NodeScriptServer nodeScriptServer,
ProjectLogConfig logConfig,
JpomApplication jpomApplication) {
this.agentWorkspaceEnvVarService = agentWorkspaceEnvVarService;
this.nodeScriptServer = nodeScriptServer;
this.logConfig = logConfig;
this.jpomApplication = jpomApplication;
}
/**
* 异步执行
*
* @param scriptProcess 脚本流程
* @param log 日志
*/
public void run(DslYmlDto.BaseProcess scriptProcess, NodeProjectInfoModel nodeProjectInfoModel, String action, String log, boolean sync) throws Exception {
DslScriptBuilder builder = this.create(scriptProcess, nodeProjectInfoModel, action, log);
Future<?> execute = ThreadUtil.execAsync(builder);
if (sync) {
execute.get();
}
}
/**
* 同步执行
*
* @param scriptProcess 脚本流程
*/
public Tuple syncRun(DslYmlDto.BaseProcess scriptProcess, NodeProjectInfoModel nodeProjectInfoModel, String action) {
try (DslScriptBuilder builder = this.create(scriptProcess, nodeProjectInfoModel, action, null)) {
return builder.syncExecute();
}
}
/**
* 解析流程脚本信息
*
* @param nodeProjectInfoModel 项目信息
* @param dslYml dsl 配置信息
* @param op 流程
* @return data
*/
public Tuple resolveProcessScript(NodeProjectInfoModel nodeProjectInfoModel, DslYmlDto dslYml, ConsoleCommandOp op) {
DslYmlDto.BaseProcess baseProcess = NodeProjectInfoModel.tryDslProcess(dslYml, op.name());
return this.resolveProcessScript(nodeProjectInfoModel, baseProcess);
}
/**
* 解析流程脚本信息
*
* @param nodeProjectInfoModel 项目信息
* @param scriptProcess 流程
* @return data
*/
public Tuple resolveProcessScript(NodeProjectInfoModel nodeProjectInfoModel, DslYmlDto.BaseProcess scriptProcess) {
JSONObject jsonObject = new JSONObject();
jsonObject.put("status", false);
if (scriptProcess == null) {
jsonObject.put("msg", "流程不存在");
return new Tuple(jsonObject, null);
}
String scriptId = scriptProcess.getScriptId();
if (StrUtil.isEmpty(scriptId)) {
jsonObject.put("msg", "请填写脚本模板id");
return new Tuple(jsonObject, null);
}
//
NodeScriptModel item = nodeScriptServer.getItem(scriptId);
if (item != null) {
// 脚本存在
jsonObject.put("status", true);
jsonObject.put("type", "script");
jsonObject.put("scriptId", scriptId);
return new Tuple(jsonObject, item);
}
File scriptFile = FileUtil.file(nodeProjectInfoModel.allLib(), scriptId);
if (FileUtil.isFile(scriptFile)) {
// 文件存在
jsonObject.put("status", true);
jsonObject.put("type", "file");
jsonObject.put("scriptId", scriptId);
return new Tuple(jsonObject, scriptFile);
}
jsonObject.put("msg", "脚本模版不存在:" + scriptId);
return new Tuple(jsonObject, null);
}
/**
* 构建 DSL 执行器
*
* @param scriptProcess 脚本流程
* @param nodeProjectInfoModel 项目
* @param log 日志路径
* @param action 具体操作
*/
private DslScriptBuilder create(DslYmlDto.BaseProcess scriptProcess, NodeProjectInfoModel nodeProjectInfoModel, String action, String log) {
Tuple tuple = this.resolveProcessScript(nodeProjectInfoModel, scriptProcess);
JSONObject jsonObject = tuple.get(0);
// 判断状态
boolean status = jsonObject.getBooleanValue("status");
cn.hutool.core.lang.Assert.isTrue(status, () -> {
String msg = jsonObject.getString("msg");
return new IllegalArgument2Exception(msg);
});
String type = jsonObject.getString("type");
EnvironmentMapBuilder environment = this.environment(nodeProjectInfoModel, scriptProcess);
File scriptFile;
boolean autoDelete = false;
if (StrUtil.equals(type, "file")) {
scriptFile = tuple.get(1);
} else {
NodeScriptModel item = tuple.get(1);
scriptFile = this.initScriptFile(item);
// 系统生成的脚本需要自动删除
autoDelete = true;
}
DslScriptBuilder builder = new DslScriptBuilder(action, environment, scriptProcess.getScriptArgs(), log, logConfig.getFileCharset());
builder.setScriptFile(scriptFile);
builder.setAutoDelete(autoDelete);
return builder;
}
/**
* 创建脚本文件
*
* @param scriptModel 脚本对象
* @return file
*/
private File initScriptFile(NodeScriptModel scriptModel) {
String dataPath = jpomApplication.getDataPath();
File scriptFile = FileUtil.file(dataPath, Const.SCRIPT_RUN_CACHE_DIRECTORY, StrUtil.format("{}.{}", IdUtil.fastSimpleUUID(), CommandUtil.SUFFIX));
// 替换内容
String context = scriptModel.getContext();
FileUtils.writeScript(context, scriptFile, ExtConfigBean.getConsoleLogCharset());
return scriptFile;
}
private EnvironmentMapBuilder environment(NodeProjectInfoModel nodeProjectInfoModel, DslYmlDto.BaseProcess scriptProcess) {
//
EnvironmentMapBuilder environmentMapBuilder = agentWorkspaceEnvVarService.getEnv(nodeProjectInfoModel.getWorkspaceId());
// 项目配置的环境变量
String dslEnv = nodeProjectInfoModel.getDslEnv();
Opt.ofBlankAble(dslEnv)
.map(s -> UrlQuery.of(s, CharsetUtil.CHARSET_UTF_8))
.map(UrlQuery::getQueryMap)
.map(map -> {
Map<String, String> map1 = MapUtil.newHashMap();
for (Map.Entry<CharSequence, CharSequence> entry : map.entrySet()) {
map1.put(StrUtil.toString(entry.getKey()), StrUtil.toString(entry.getValue()));
}
return map1;
})
.ifPresent(environmentMapBuilder::putStr);
//
environmentMapBuilder
.putStr(scriptProcess.getScriptEnv())
.put("PROJECT_ID", nodeProjectInfoModel.getId())
.put("PROJECT_NAME", nodeProjectInfoModel.getName())
.put("PROJECT_PATH", nodeProjectInfoModel.allLib());
return environmentMapBuilder;
}
}

View File

@ -147,7 +147,7 @@ public class AgentWebSocketConsoleHandle extends BaseAgentWebSocketHandle {
RunMode runMode = nodeProjectInfoModel.getRunMode();
if (runMode == RunMode.Dsl) {
// 判断是否可以执行 reload 事件
DslYmlDto dslYmlDto = nodeProjectInfoModel.dslConfig();
DslYmlDto dslYmlDto = nodeProjectInfoModel.mustDslConfig();
boolean b = dslYmlDto.hasRunProcess(ConsoleCommandOp.reload.name());
json.put("canReload", b);
}

View File

@ -216,13 +216,14 @@ public class JpomApplication implements DisposableBean, InitializingBean {
if (SystemUtil.getOsInfo().isWindows()) {
//String result = CommandUtil.execSystemCommand(command, scriptFile.getParentFile());
//log.debug("windows restart {}", result);
CommandUtil.asyncExeLocalCommand(parentFile, "start /b" + command);
CommandUtil.asyncExeLocalCommand("start /b" + command, parentFile);
} else {
String jpomService = SystemUtil.get("JPOM_SERVICE");
if (StrUtil.isEmpty(jpomService)) {
CommandUtil.asyncExeLocalCommand(parentFile, command);
CommandUtil.asyncExeLocalCommand(command, parentFile);
} else {
CommandUtil.asyncExeLocalCommand(parentFile, "systemctl restart " + jpomService);
// 使用了服务
CommandUtil.asyncExeLocalCommand("systemctl restart " + jpomService, parentFile);
}
}
} catch (Exception e) {

View File

@ -39,6 +39,9 @@ public enum ConsoleCommandOp {
stop(true),
restart(true),
status,
/**
* 重载
*/
reload(true),
/**
* 运行日志
@ -49,7 +52,9 @@ public enum ConsoleCommandOp {
*/
heart,
;
/**
* 是否支持手动操作执行
*/
private final boolean canOpt;
ConsoleCommandOp() {

View File

@ -266,7 +266,7 @@ public class CommandUtil {
* @param command 命令
* @throws IOException 异常
*/
public static void asyncExeLocalCommand(File file, String command) throws Exception {
public static void asyncExeLocalCommand(String command, File file) throws Exception {
String newCommand = StrUtil.replace(command, StrUtil.CRLF, StrUtil.SPACE);
newCommand = StrUtil.replace(newCommand, StrUtil.LF, StrUtil.SPACE);
//

View File

@ -30,7 +30,6 @@ import org.dromara.jpom.common.JpomManifest;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.function.Supplier;
import java.util.stream.Collectors;
@ -114,11 +113,15 @@ public class JvmUtil {
public static Integer getPidByTag(String tag) {
String execSystemCommand = CommandUtil.execSystemCommand("jps -mv");
List<String> list = StrSplitter.splitTrim(execSystemCommand, StrUtil.LF, true);
Optional<String> any = list.stream().filter(s -> checkCommandLineIsJpom(s, tag)).map(s -> {
List<String> split = StrUtil.split(s, StrUtil.SPACE);
return CollUtil.getFirst(split);
}).findAny();
return any.map(Convert::toInt).orElse(null);
return list.stream()
.filter(s -> checkCommandLineIsJpom(s, tag))
.map(s -> {
List<String> split = StrUtil.split(s, StrUtil.SPACE);
return CollUtil.getFirst(split);
})
.findAny()
.map(Convert::toInt)
.orElse(null);
}
/**

View File

@ -34,6 +34,7 @@ import cn.hutool.db.Page;
import cn.hutool.db.PageResult;
import cn.hutool.db.ds.DSFactory;
import cn.hutool.db.sql.Condition;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.dromara.jpom.model.PageResultDto;
import org.dromara.jpom.system.JpomRuntimeException;
@ -69,6 +70,7 @@ public abstract class BaseDbCommonService<T> {
/**
* 表名
*/
@Getter
protected final String tableName;
protected final Class<T> tClass;
@ -81,10 +83,6 @@ public abstract class BaseDbCommonService<T> {
this.tableName = annotation.value();
}
public String getTableName() {
return tableName;
}
protected DataSource getDataSource() {
DSFactory dsFactory = StorageServiceFactory.get().getDsFactory();
return dsFactory.getDataSource();

View File

@ -95,10 +95,6 @@ export default {
padding-bottom: 20px;
}
.node-full-content {
min-height: calc(100vh - 130px) !important;
}
.globalLoading {
height: 100vh;
width: 100vw;

View File

@ -498,6 +498,15 @@ export function getProjectGroupAll() {
*/
export const runModeList = ["Dsl", "ClassPath", "Jar", "JarWar", "JavaExtDirsCp", "File"];
export const runModeObj = {
Dsl: "自定义脚本项目python、nodejs、go、接口探活、es【推荐】",
ClassPath: "Java 项目java -classpath",
Jar: "Java 项目java -jar xxx",
JavaExtDirsCp: "Java 项目java -Djava.ext.dirs=lib -cp conf:run.jar $MAIN_CLASS",
File: "静态文件项目(前端、日志等)",
JarWar: "Java 项目java -jar Springboot war【不推荐】",
};
/**
* java 项目的运行模式
*/

View File

@ -1,7 +1,7 @@
<template>
<div>
<!-- 布局 -->
<a-layout class="file-layout node-full-content">
<a-layout class="file-layout">
<!-- 目录树 -->
<a-layout-sider theme="light" class="sider" width="25%">
<div class="dir-container">

View File

@ -1,302 +0,0 @@
<template>
<div class="node-full-content">
<!-- 数据表格 -->
<a-table size="middle" :data-source="list" :columns="columns" @change="changePage" :pagination="pagination" bordered rowKey="id">
<template slot="title">
<a-space>
<a-input v-model="listQuery['%name%']" placeholder="名称" allowClear class="search-input-item" />
<a-tooltip title="按住 Ctr 或者 Alt/Option 键点击按钮快速回到第一页">
<a-button type="primary" :loading="loading" @click="loadData">搜索</a-button>
</a-tooltip>
<a-button type="primary" @click="handleAdd">新增</a-button>
<a-tooltip placement="topLeft" title="清除服务端缓存节点所有的脚步模版信息并重新同步">
<a-icon @click="sync()" type="sync" spin />
</a-tooltip>
</a-space>
</template>
<a-tooltip slot="tooltip" slot-scope="text" placement="topLeft" :title="text">
<span>{{ text }}</span>
</a-tooltip>
<template slot="global" slot-scope="text">
<a-tag v-if="text === 'GLOBAL'">全局</a-tag>
<a-tag v-else>工作空间</a-tag>
</template>
<template slot="operation" slot-scope="text, record">
<a-space>
<a-button size="small" type="primary" @click="handleExec(record)">执行</a-button>
<a-button size="small" :type="`${record.scriptType === 'server-sync' ? '' : 'primary'}`" @click="handleEdit(record)">{{ record.scriptType === "server-sync" ? "查看" : " 编辑" }}</a-button>
<a-tooltip :title="`${record.scriptType === 'server-sync' ? '服务端分发同步的脚本不能直接删除,需要到服务端去操作' : '删除'}`">
<a-button size="small" :disabled="record.scriptType === 'server-sync'" type="danger" @click="handleDelete(record)">删除</a-button>
</a-tooltip>
</a-space>
</template>
</a-table>
<!-- 编辑区 -->
<a-modal destroyOnClose v-model="editScriptVisible" title="编辑 Script" @ok="handleEditScriptOk" :maskClosable="false" width="80vw">
<a-form-model ref="editScriptForm" :rules="rules" :model="temp" :label-col="{ span: 3 }" :wrapper-col="{ span: 18 }">
<a-alert v-if="this.temp.scriptType === 'server-sync'" message="服务端同步的脚本不能在此修改" banner />
<a-form-model-item v-if="temp.id" label="ScriptId" prop="id">
<a-input v-model="temp.id" disabled readOnly />
</a-form-model-item>
<a-form-model-item label="Script 名称" prop="name">
<a-input :maxLength="50" v-model="temp.name" placeholder="名称" />
</a-form-model-item>
<a-form-model-item label="Script 内容" prop="context">
<div style="height: 40vh; overflow-y: scroll">
<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-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="global">
<a-radio-group v-model="temp.global">
<a-radio :value="true"> 全局</a-radio>
<a-radio :value="false"> 当前工作空间</a-radio>
</a-radio-group>
</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">
<template slot="dataSource">
<a-select-opt-group v-for="group in cronDataSource" :key="group.title">
<span slot="label">
{{ group.title }}
</span>
<a-select-option v-for="opt in group.children" :key="opt.title" :value="opt.value"> {{ opt.title }} {{ opt.value }} </a-select-option>
</a-select-opt-group>
</template>
</a-auto-complete>
</a-form-model-item>
<a-form-model-item label="描述" prop="description">
<a-input :maxLength="200" v-model="temp.description" type="textarea" :rows="3" style="resize: none" placeholder="详细描述" />
</a-form-model-item>
</a-form-model>
</a-modal>
<!-- 脚本控制台组件 -->
<a-drawer destroyOnClose :title="drawerTitle" placement="right" width="85vw" :visible="drawerConsoleVisible" @close="onConsoleClose">
<script-console v-if="drawerConsoleVisible" :nodeId="node.id" :defArgs="temp.defArgs" :id="temp.id" :scriptId="temp.scriptId" />
</a-drawer>
</div>
</template>
<script>
import { getScriptList, editScript, deleteScript, itemScript, syncScript } from "@/api/node-other";
import codeEditor from "@/components/codeEditor";
import ScriptConsole from "./script-console";
import { CRON_DATA_SOURCE, COMPUTED_PAGINATION, CHANGE_PAGE, PAGE_DEFAULT_LIST_QUERY, parseTime } from "@/utils/const";
export default {
components: {
ScriptConsole,
codeEditor,
},
props: {
node: {
type: Object,
},
},
data() {
return {
loading: false,
listQuery: Object.assign({}, PAGE_DEFAULT_LIST_QUERY),
list: [],
temp: {},
cronDataSource: CRON_DATA_SOURCE,
editScriptVisible: false,
drawerTitle: "",
drawerConsoleVisible: false,
columns: [
{ title: "Script ID", dataIndex: "scriptId", width: 150, ellipsis: true, scopedSlots: { customRender: "tooltip" } },
{ title: "名称", dataIndex: "name", ellipsis: true, width: 200, scopedSlots: { customRender: "tooltip" } },
{ title: "定时执行", dataIndex: "autoExecCron", ellipsis: true, width: "120px", scopedSlots: { customRender: "autoExecCron" } },
{ title: "共享", dataIndex: "workspaceId", ellipsis: true, scopedSlots: { customRender: "global" }, width: "90px" },
{
title: "创建时间",
dataIndex: "createTimeMillis",
ellipsis: true,
sorter: true,
customRender: (text) => parseTime(text),
width: "170px",
},
{
title: "修改时间",
dataIndex: "modifyTimeMillis",
width: "170px",
ellipsis: true,
sorter: true,
customRender: (text) => parseTime(text),
},
{ title: "创建人", dataIndex: "createUser", ellipsis: true, scopedSlots: { customRender: "tooltip" }, width: "120px" },
{ title: "修改人", dataIndex: "modifyUser", ellipsis: true, scopedSlots: { customRender: "modifyUser" }, width: "120px" },
// { title: "", dataIndex: "lastRunUser", ellipsis: true, width: 150, scopedSlots: { customRender: "lastRunUser" } },
{ title: "操作", dataIndex: "operation", align: "center", scopedSlots: { customRender: "operation" }, fixed: "right", width: "180px" },
],
rules: {
name: [{ required: true, message: "请输入脚本名称", trigger: "blur" }],
context: [{ required: true, message: "请输入脚本内容", trigger: "blur" }],
},
commandParams: [],
};
},
computed: {
pagination() {
return COMPUTED_PAGINATION(this.listQuery);
},
},
mounted() {
// this.calcTableHeight();
this.loadData();
},
methods: {
//
loadData(pointerEvent) {
this.listQuery.page = pointerEvent?.altKey || pointerEvent?.ctrlKey ? 1 : this.listQuery.page;
this.loading = true;
getScriptList({ ...this.listQuery, nodeId: this.node.id }).then((res) => {
if (res.code === 200) {
this.list = res.data.result;
this.listQuery.total = res.data.total;
}
this.loading = false;
});
},
parseTime,
//
handleAdd() {
this.temp = {
type: "add",
};
this.commandParams = [];
this.editScriptVisible = true;
},
//
handleEdit(record) {
itemScript({
id: record.scriptId,
nodeId: this.node.id,
}).then((res) => {
this.temp = Object.assign({}, res.data, { global: res.data.workspaceId === "GLOBAL", workspaceId: "" });
this.commandParams = this.temp.defArgs ? JSON.parse(this.temp.defArgs) : [];
//
this.editScriptVisible = true;
});
},
// Script
handleEditScriptOk() {
if (this.temp.scriptType === "server-sync") {
this.$notification.warning({
message: "服务端同步的脚本不能在此修改",
});
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) {
return false;
}
this.temp.nodeId = this.node.id;
//
editScript(this.temp).then((res) => {
if (res.code === 200) {
//
this.$notification.success({
message: res.msg,
});
this.editScriptVisible = false;
this.loadData();
this.$refs["editScriptForm"].resetFields();
}
});
});
},
handleDelete(record) {
this.$confirm({
title: "系统提示",
content: "真的要删除脚本么?",
okText: "确认",
cancelText: "取消",
onOk: () => {
//
const params = {
nodeId: this.node.id,
id: record.scriptId,
};
//
deleteScript(params).then((res) => {
if (res.code === 200) {
this.$notification.success({
message: res.msg,
});
this.loadData();
}
});
},
});
},
// Script
handleExec(record) {
this.temp = Object.assign({}, record);
this.drawerTitle = `控制台(${this.temp.name})`;
this.drawerConsoleVisible = true;
},
// console
onConsoleClose() {
this.drawerConsoleVisible = false;
},
//
changePage(pagination, filters, sorter) {
this.listQuery = CHANGE_PAGE(this.listQuery, { pagination, sorter });
this.loadData();
},
sync() {
syncScript({
nodeId: this.node.id,
}).then((res) => {
if (res.code == 200) {
this.$notification.success({
message: res.msg,
});
this.loadData();
}
});
},
},
};
</script>
<style scoped></style>

View File

@ -1,5 +1,5 @@
<template>
<div class="node-full-content">
<div>
<!-- 数据表格 -->
<a-table :data-source="list" size="middle" :columns="columns" @change="changePage" :pagination="pagination" bordered rowKey="id">
<template slot="title">

View File

@ -10,33 +10,60 @@
<a-button size="small" v-if="project.runMode === 'Dsl'" :disabled="!canReload" :loading="optButtonLoading" type="primary" @click="reload">重载</a-button>
<a-button size="small" type="primary" @click="goFile">文件管理</a-button>
<a-dropdown>
<!-- <a type="link" class="ant-dropdown-link"> 更多<a-icon type="down" /> </a> -->
<a-button
size="small"
@click="
(e) => {
e.preventDefault();
handleLogBack();
}
"
>
<!-- <a-tag> -->
日志大小: {{ project.logSize || "-" }}
<!-- 更多 -->
<a-icon type="fullscreen" />
<!-- </a-tag> -->
</a-button>
<!-- <a-menu slot="overlay">
<a-menu-item>
<a-button type="primary" size="small" :disabled="!project.logSize" @click="handleDownload">导出日志</a-button>
<a-dropdown v-if="project.dslProcessInfo">
<a-menu slot="overlay">
<a-menu-item v-for="(item, index) in project.dslProcessInfo" :key="index">
<template v-if="item.status">
<a-tag>
{{ item.process }}
</a-tag>
<template v-if="item.type === 'file'">项目文件 {{ item.scriptId }} </template>
<template v-else-if="item.type === 'script'">
<a-button
type="link"
size="small"
icon="edit"
@click="
() => {
temp = { scriptId: item.scriptId };
editScriptVisible = true;
}
"
>
节点脚本
</a-button>
</template>
</template>
<template v-else>
<a-space>
<a-tag>
{{ item.process }}
</a-tag>
<a-icon type="exclamation-circle" />
{{ item.msg }}
</a-space>
</template>
</a-menu-item>
<a-menu-item>
<a-button type="primary" size="small" @click="handleLogBack">备份列表</a-button>
</a-menu-item>
</a-menu> -->
</a-menu>
<a-button size="small" type="primary"> 关联脚本 <a-icon type="down" /> </a-button>
</a-dropdown>
<a-button
size="small"
@click="
(e) => {
e.preventDefault();
handleLogBack();
}
"
>
<!-- <a-tag> -->
日志大小: {{ project.logSize || "-" }}
<!-- 更多 -->
<a-icon type="fullscreen" />
<!-- </a-tag> -->
</a-button>
|
</a-space>
</template>
</log-view>
@ -44,6 +71,17 @@
<a-modal destroyOnClose v-model="lobbackVisible" title="日志备份列表" width="850px" :footer="null" :maskClosable="false">
<ProjectLog v-if="lobbackVisible" :nodeId="this.nodeId" :projectId="this.projectId"></ProjectLog>
</a-modal>
<!-- 编辑区 -->
<ScriptEdit
v-if="editScriptVisible"
:nodeId="this.nodeId"
:scriptId="temp.scriptId"
@close="
() => {
editScriptVisible = false;
}
"
></ScriptEdit>
</div>
</template>
<script>
@ -51,12 +89,13 @@ import { getProjectData, getProjectLogSize } from "@/api/node-project";
import { mapGetters } from "vuex";
import { getWebSocketUrl } from "@/utils/const";
import LogView from "@/components/logView/index2";
import ProjectLog from "./project-log.vue";
import ProjectLog from "./project-log";
import ScriptEdit from "@/pages/node/script-edit";
export default {
components: {
LogView,
ProjectLog,
ScriptEdit,
},
props: {
nodeId: {
@ -79,6 +118,7 @@ export default {
lobbackVisible: false,
canReload: false,
heart: null,
editScriptVisible: false,
};
},
computed: {

View File

@ -52,7 +52,16 @@
</a-tooltip>
</template>
<a-select v-model="temp.runMode" placeholder="请选择运行方式">
<a-select-option v-for="runMode in runModeList" :key="runMode">{{ runMode }}</a-select-option>
<a-select-option v-for="(val, key) in runModeObj" :key="key">
<template v-if="val.indexOf('不推荐') > -1">
<s>
<b>[{{ key }}]</b> {{ val }}
</s>
</template>
<template v-else>
<b>[{{ key }}]</b> {{ val }}
</template>
</a-select-option>
</a-select>
</a-form-model-item>
<a-form-model-item prop="whitelistDirectory" class="jpom-node-project-whitelist">
@ -178,7 +187,9 @@
<a-icon v-show="temp.type !== 'edit'" type="question-circle" theme="filled" />
</a-tooltip>
</template>
<template #help>非服务器开机自启,如需开机自启建议配置<b>插件端开机自启</b>并开启此开关</template>
<a-switch v-model="temp.autoStart" checked-children="" un-checked-children="" />
插件端启动时自动检查项目如未启动将尝试启动
</a-form-model-item>
<a-form-model-item v-if="temp.runMode === 'Dsl'" prop="dslEnv" label="DSL环境变量">
<a-input v-model="temp.dslEnv" placeholder="DSL环境变量,如key1=values1&keyvalue2" />
@ -244,6 +255,7 @@ import {
getProjectData,
javaModes,
// nodeJudgeLibExist,
runModeObj,
noFileModes,
runModeList,
getProjectGroupAll,
@ -269,9 +281,10 @@ export default {
return {
accessList: [],
groupList: [],
runModeList: runModeList,
javaModes: javaModes,
noFileModes: noFileModes,
runModeObj,
runModeList,
javaModes,
noFileModes,
PROJECT_DSL_DEFATUL,
configDir: false,
temp: {},

View File

@ -18,7 +18,7 @@
</a-table>
</div>
<!-- 布局 -->
<a-layout v-show="!viewList" class="file-layout node-full-content">
<a-layout v-show="!viewList" class="file-layout">
<!-- 目录树 -->
<a-layout-sider theme="light" class="sider" width="25%">
<div class="dir-container">

View File

@ -1,7 +1,7 @@
<template>
<div>
<!-- 布局 -->
<a-layout class="file-layout node-full-content">
<a-layout class="file-layout">
<!-- 目录树 -->
<a-layout-sider theme="light" class="sider" width="25%">
<div class="dir-container">

View File

@ -1,5 +1,5 @@
<template>
<div class="node-full-content">
<div class="">
<a-tabs default-active-key="1" tab-position="left">
<a-tab-pane key="1" tab="缓存信息">
<a-alert message="请勿手动删除数据目录下面文件,如果需要删除需要提前备份或者已经确定对应文件弃用后才能删除" style="margin-top: 10px; margin-bottom: 40px" banner />

View File

@ -1,5 +1,5 @@
<template>
<div class="node-full-content">
<div class="">
<a-form-model ref="editForm" :model="temp">
<a-alert v-if="temp.file" :message="`配置文件路径:${temp.file}`" style="margin-top: 10px; margin-bottom: 20px" banner />
<a-form-model-item class="node-content-config">

View File

@ -1,5 +1,5 @@
<template>
<a-layout class="log-layout node-full-content">
<a-layout class="log-layout">
<!-- 侧边栏 文件树 -->
<a-layout-sider theme="light" class="log-sider jpom-node-log-tree" width="20%">
<a-empty v-if="list.length === 0" />

View File

@ -1,27 +0,0 @@
<template>
<div class="node-full-content">
<machine-info :nodeId="this.node.id"></machine-info>
</div>
</template>
<script>
import machineInfo from "@/pages/system/assets/machine/machine-info";
export default {
components: {
machineInfo,
},
props: {
node: {
type: Object,
},
},
data() {
return {};
},
mounted() {},
destroyed() {},
watch: {},
methods: {},
};
</script>
<style scoped></style>

View File

@ -0,0 +1,195 @@
<template>
<div>
<a-modal
destroyOnClose
:visible="true"
title="编辑 Script"
@ok="handleEditScriptOk"
:maskClosable="false"
width="80vw"
@cancel="
() => {
$emit('close');
}
"
>
<a-form-model ref="editScriptForm" :rules="rules" :model="temp" :label-col="{ span: 3 }" :wrapper-col="{ span: 19 }">
<a-alert v-if="this.temp.scriptType === 'server-sync'" message="服务端同步的脚本不能在此修改" banner />
<a-form-model-item label="选择节点">
<a-select v-model="temp.nodeId" :disabled="!!temp.nodeId" allowClear placeholder="请选择节点">
<a-select-option v-for="node in nodeList" :key="node.id">{{ node.name }}</a-select-option>
</a-select>
</a-form-model-item>
<template v-if="temp.nodeId">
<a-form-model-item label="Script 名称" prop="name">
<a-input v-model="temp.name" placeholder="名称" />
</a-form-model-item>
<a-form-model-item label="Script 内容" prop="context">
<div style="height: 40vh; overflow-y: scroll">
<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-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="global">
<a-radio-group v-model="temp.global">
<a-radio :value="true"> 全局</a-radio>
<a-radio :value="false"> 当前工作空间</a-radio>
</a-radio-group>
</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"
>
<template slot="dataSource">
<a-select-opt-group v-for="group in cronDataSource" :key="group.title">
<span slot="label">
{{ group.title }}
</span>
<a-select-option v-for="opt in group.children" :key="opt.title" :value="opt.value"> {{ opt.title }} {{ opt.value }} </a-select-option>
</a-select-opt-group>
</template>
</a-auto-complete>
</a-form-model-item>
<a-form-model-item label="描述" prop="description">
<a-input v-model="temp.description" type="textarea" :rows="3" style="resize: none" placeholder="详细描述" />
</a-form-model-item>
</template>
</a-form-model>
</a-modal>
</div>
</template>
<script>
import codeEditor from "@/components/codeEditor";
import { editScript, itemScript } from "@/api/node-other";
import { CRON_DATA_SOURCE } from "@/utils/const";
import { getNodeListAll } from "@/api/node";
export default {
components: {
codeEditor,
},
props: {
nodeId: {
type: String,
default: undefined,
},
scriptId: {
type: String,
default: "",
},
},
data() {
return {
temp: {},
cronDataSource: CRON_DATA_SOURCE,
commandParams: [],
nodeList: [],
rules: {
name: [{ required: true, message: "请输入脚本名称", trigger: "blur" }],
context: [{ required: true, message: "请输入脚本内容", trigger: "blur" }],
},
};
},
mounted() {
//
getNodeListAll().then((res) => {
if (res.code === 200 && res.data) {
this.nodeList = res.data;
// res.data.forEach((item) => {
// this.nodeMap[item.id] = item.name;
// });
}
this.handleEdit();
});
},
methods: {
//
handleEdit() {
this.$refs["editScriptForm"]?.resetFields();
if (this.scriptId && this.nodeId) {
itemScript({
id: this.scriptId,
nodeId: this.nodeId,
}).then((res) => {
if (res.code === 200) {
this.temp = Object.assign({}, res.data, { global: res.data.workspaceId === "GLOBAL", workspaceId: "" });
this.temp.nodeId = this.nodeId;
this.commandParams = this.temp.defArgs ? JSON.parse(this.temp.defArgs) : [];
//
}
});
} else {
this.temp = { global: false, type: "add", nodeId: this.nodeId };
}
},
// Script
handleEditScriptOk() {
if (this.temp.scriptType === "server-sync") {
this.$notification.warning({
message: "服务端同步的脚本不能在此修改",
});
return;
}
if (!this.temp.nodeId) {
this.$notification.warning({
message: "没有选择节点不能保存脚本",
});
return;
}
//
this.$refs["editScriptForm"].validate((valid) => {
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) {
//
this.$notification.success({
message: res.msg,
});
this.$emit("close");
}
});
});
},
},
};
</script>

View File

@ -1,5 +1,5 @@
<template>
<div class="node-full-content">
<div class="">
<!-- 数据表格 -->
<a-table :data-source="list" size="middle" :columns="columns" @change="changePage" :pagination="pagination" bordered rowKey="id">
<template slot="title">
@ -85,74 +85,16 @@
</template>
</a-table>
<!-- 编辑区 -->
<a-modal destroyOnClose v-model="editScriptVisible" title="编辑 Script" @ok="handleEditScriptOk" :maskClosable="false" width="80vw">
<a-form-model ref="editScriptForm" :rules="rules" :model="temp" :label-col="{ span: 3 }" :wrapper-col="{ span: 19 }">
<a-alert v-if="this.temp.scriptType === 'server-sync'" message="服务端同步的脚本不能在此修改" banner />
<a-form-model-item label="选择节点" v-if="!temp.nodeId">
<a-select v-model="temp.nodeId" :disabled="!!temp.nodeId" allowClear placeholder="请选择节点">
<a-select-option v-for="(nodeName, key) in nodeMap" :key="key">{{ nodeName }}</a-select-option>
</a-select>
</a-form-model-item>
<template v-if="temp.nodeId">
<a-form-model-item label="Script 名称" prop="name">
<a-input v-model="temp.name" placeholder="名称" />
</a-form-model-item>
<a-form-model-item label="Script 内容" prop="context">
<div style="height: 40vh; overflow-y: scroll">
<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-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="global">
<a-radio-group v-model="temp.global">
<a-radio :value="true"> 全局</a-radio>
<a-radio :value="false"> 当前工作空间</a-radio>
</a-radio-group>
</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"
>
<template slot="dataSource">
<a-select-opt-group v-for="group in cronDataSource" :key="group.title">
<span slot="label">
{{ group.title }}
</span>
<a-select-option v-for="opt in group.children" :key="opt.title" :value="opt.value"> {{ opt.title }} {{ opt.value }} </a-select-option>
</a-select-opt-group>
</template>
</a-auto-complete>
</a-form-model-item>
<a-form-model-item label="描述" prop="description">
<a-input v-model="temp.description" type="textarea" :rows="3" style="resize: none" placeholder="详细描述" />
</a-form-model-item>
</template>
</a-form-model>
</a-modal>
<ScriptEdit
v-if="editScriptVisible"
:nodeId="temp.nodeId"
:scriptId="temp.scriptId"
@close="
() => {
editScriptVisible = false;
}
"
></ScriptEdit>
<!-- 脚本控制台组件 -->
<a-drawer
:title="drawerTitle"
@ -249,18 +191,19 @@
</div>
</template>
<script>
import { delAllCache, deleteScript, editScript, getScriptListAll, itemScript, getTriggerUrl, unbindScript, syncScript } from "@/api/node-other";
import codeEditor from "@/components/codeEditor";
import { delAllCache, deleteScript, getScriptListAll, getTriggerUrl, unbindScript, syncScript } from "@/api/node-other";
import { getNodeListAll } from "@/api/node";
import ScriptConsole from "@/pages/node/node-layout/other/script-console";
import { CHANGE_PAGE, COMPUTED_PAGINATION, CRON_DATA_SOURCE, PAGE_DEFAULT_LIST_QUERY, parseTime } from "@/utils/const";
import { CHANGE_PAGE, COMPUTED_PAGINATION, PAGE_DEFAULT_LIST_QUERY, parseTime } from "@/utils/const";
import ScriptLog from "@/pages/node/node-layout/other/script-log";
import ScriptEdit from "@/pages/node/script-edit";
import Vue from "vue";
export default {
components: {
ScriptConsole,
codeEditor,
ScriptEdit,
ScriptLog,
},
props: {
@ -273,7 +216,7 @@ export default {
return {
loading: false,
listQuery: Object.assign({}, PAGE_DEFAULT_LIST_QUERY),
cronDataSource: CRON_DATA_SOURCE,
list: [],
temp: {},
nodeMap: {},
@ -310,12 +253,8 @@ export default {
{ title: "最后操作人", dataIndex: "lastRunUser", ellipsis: true, scopedSlots: { customRender: "lastRunUser" }, width: 120 },
{ title: "操作", dataIndex: "operation", align: "center", scopedSlots: { customRender: "operation" }, fixed: "right", width: "240px" },
],
rules: {
name: [{ required: true, message: "请输入脚本名称", trigger: "blur" }],
context: [{ required: true, message: "请输入脚本内容", trigger: "blur" }],
},
triggerVisible: false,
commandParams: [],
};
},
computed: {
@ -350,72 +289,15 @@ export default {
});
},
parseTime,
//
//
handleEdit(record) {
this.$refs["editScriptForm"]?.resetFields();
if (record) {
itemScript({
id: record.scriptId,
nodeId: record.nodeId,
}).then((res) => {
if (res.code === 200) {
this.temp = Object.assign({}, res.data, { global: res.data.workspaceId === "GLOBAL", workspaceId: "" });
this.temp.nodeId = record.nodeId;
this.commandParams = this.temp.defArgs ? JSON.parse(this.temp.defArgs) : [];
//
this.editScriptVisible = true;
}
});
this.temp = { ...record };
} else {
this.temp = { global: false, type: "add", nodeId: this.listQuery.nodeId };
this.editScriptVisible = true;
this.temp = { nodeId: this.listQuery.nodeId };
}
},
// Script
handleEditScriptOk() {
if (this.temp.scriptType === "server-sync") {
this.$notification.warning({
message: "服务端同步的脚本不能在此修改",
});
return;
}
if (!this.temp.nodeId) {
this.$notification.warning({
message: "没有选择节点不能保存脚本",
});
return;
}
//
this.$refs["editScriptForm"].validate((valid) => {
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) {
//
this.$notification.success({
message: res.msg,
});
this.editScriptVisible = false;
this.loadData();
}
});
});
this.editScriptVisible = true;
},
handleDelete(record) {
this.$confirm({

View File

@ -1,5 +1,5 @@
<template>
<div class="node-full-content">
<div class="">
<!-- 数据表格 -->
<a-table :data-source="list" size="middle" :columns="columns" @change="changePage" :pagination="pagination" bordered rowKey="id">
<template slot="title">

View File

@ -234,6 +234,12 @@ export const PROJECT_DSL_DEFATUL =
"# scriptArgs: restart\r\n" +
"# scriptEnv:\r\n" +
'# "boot_active": test\r\n' +
"# reload:\r\n" +
"## scriptId: project.sh\r\n" +
"# scriptId: \r\n" +
"# scriptArgs: reload\r\n" +
"# scriptEnv:\r\n" +
'# "boot_active": test\r\n' +
"file:\r\n" +
"# 备份文件保留个数\r\n" +
"# backupCount: 5\r\n" +