🚀 feat(server): 文件发布支持选择脚本模板

Closes I9P0EU

#I9P0EU
This commit is contained in:
小吾立 2024-05-31 09:27:52 +08:00
parent f1481ab14a
commit 83f13222ca
4 changed files with 172 additions and 19 deletions

View File

@ -6,6 +6,7 @@
1. 【server】优化 部分参数、环境变量配置交互优化取消文本输入框采用标签模式(感谢@湘江夜色)
2. 【server】修复 部分页面中文描述未正常显示
3. 【server】优化 文件发布支持选择脚本模板(感谢[@linCodeTest](https://gitee.com/linWorld) [Gitee issues I9P0EU](https://gitee.com/dromara/Jpom/issues/I9P0EU)
------

View File

@ -16,6 +16,7 @@ import cn.keepbx.jpom.IJsonMessage;
import cn.keepbx.jpom.model.JsonMessage;
import com.alibaba.fastjson2.JSONObject;
import org.dromara.jpom.common.BaseServerController;
import org.dromara.jpom.common.ServerConst;
import org.dromara.jpom.common.validator.ValidatorItem;
import org.dromara.jpom.common.validator.ValidatorRule;
import org.dromara.jpom.controller.outgiving.OutGivingWhitelistService;
@ -24,10 +25,12 @@ import org.dromara.jpom.func.files.service.FileReleaseTaskService;
import org.dromara.jpom.model.PageResultDto;
import org.dromara.jpom.model.data.AgentWhitelist;
import org.dromara.jpom.model.data.ServerWhitelist;
import org.dromara.jpom.model.script.ScriptModel;
import org.dromara.jpom.permission.ClassFeature;
import org.dromara.jpom.permission.Feature;
import org.dromara.jpom.permission.MethodFeature;
import org.dromara.jpom.service.node.NodeService;
import org.dromara.jpom.service.script.ScriptServer;
import org.dromara.jpom.util.FileUtils;
import org.springframework.http.MediaType;
import org.springframework.util.Assert;
@ -52,12 +55,15 @@ public class FileReleaseTaskController extends BaseServerController {
private final FileReleaseTaskService fileReleaseTaskService;
private final OutGivingWhitelistService outGivingWhitelistService;
private final ScriptServer scriptServer;
public FileReleaseTaskController(FileReleaseTaskService fileReleaseTaskService,
OutGivingWhitelistService outGivingWhitelistService,
NodeService nodeService) {
NodeService nodeService,
ScriptServer scriptServer) {
this.fileReleaseTaskService = fileReleaseTaskService;
this.outGivingWhitelistService = outGivingWhitelistService;
this.scriptServer = scriptServer;
this.nodeService = nodeService;
}
@ -80,6 +86,17 @@ public class FileReleaseTaskController extends BaseServerController {
Assert.state(AgentWhitelist.checkPath(whitelistServerOutGiving, releasePathParent), "请选择正确的项目路径,或者还没有配置授权");
Assert.hasText(releasePathSecondary, "请填写发布文件的二级目录");
if (StrUtil.startWith(beforeScript, ServerConst.REF_SCRIPT)) {
String scriptId = StrUtil.removePrefix(beforeScript, ServerConst.REF_SCRIPT);
ScriptModel keyAndGlobal = scriptServer.getByKeyAndGlobal(scriptId, request, "请选择正确的发布前脚本");
Assert.notNull(keyAndGlobal, "请选择正确的发布前脚本");
}
if (StrUtil.startWith(afterScript, ServerConst.REF_SCRIPT)) {
String scriptId = StrUtil.removePrefix(afterScript, ServerConst.REF_SCRIPT);
ScriptModel keyAndGlobal = scriptServer.getByKeyAndGlobal(scriptId, request, "请选择正确的发布后脚本");
Assert.notNull(keyAndGlobal, "请选择正确的发布后脚本");
}
String releasePath = FileUtil.normalize(releasePathParent + StrUtil.SLASH + releasePathSecondary);
return fileReleaseTaskService.addTask(fileId, fileType, name, taskType, taskDataIds, releasePath, beforeScript, afterScript, null, request);

View File

@ -29,6 +29,7 @@ import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.Session;
import lombok.extern.slf4j.Slf4j;
import org.dromara.jpom.JpomApplication;
import org.dromara.jpom.common.ServerConst;
import org.dromara.jpom.common.forward.NodeForward;
import org.dromara.jpom.common.forward.NodeUrl;
import org.dromara.jpom.func.assets.model.MachineSshModel;
@ -40,11 +41,13 @@ import org.dromara.jpom.model.EnvironmentMapBuilder;
import org.dromara.jpom.model.PageResultDto;
import org.dromara.jpom.model.data.NodeModel;
import org.dromara.jpom.model.data.SshModel;
import org.dromara.jpom.model.script.ScriptModel;
import org.dromara.jpom.plugins.JschUtils;
import org.dromara.jpom.service.IStatusRecover;
import org.dromara.jpom.service.h2db.BaseWorkspaceService;
import org.dromara.jpom.service.node.NodeService;
import org.dromara.jpom.service.node.ssh.SshService;
import org.dromara.jpom.service.script.ScriptServer;
import org.dromara.jpom.service.system.WorkspaceEnvVarService;
import org.dromara.jpom.system.ServerConfig;
import org.dromara.jpom.configuration.BuildExtConfig;
@ -81,6 +84,7 @@ public class FileReleaseTaskService extends BaseWorkspaceService<FileReleaseTask
private final ServerConfig serverConfig;
private final FileStorageService fileStorageService;
private final StaticFileStorageService staticFileStorageService;
private final ScriptServer scriptServer;
private final Map<String, String> cancelTag = new SafeConcurrentHashMap<>();
@ -91,7 +95,8 @@ public class FileReleaseTaskService extends BaseWorkspaceService<FileReleaseTask
BuildExtConfig buildExtConfig,
ServerConfig serverConfig,
FileStorageService fileStorageService,
StaticFileStorageService staticFileStorageService) {
StaticFileStorageService staticFileStorageService,
ScriptServer scriptServer) {
this.sshService = sshService;
this.jpomApplication = jpomApplication;
this.workspaceEnvVarService = workspaceEnvVarService;
@ -100,6 +105,7 @@ public class FileReleaseTaskService extends BaseWorkspaceService<FileReleaseTask
this.serverConfig = serverConfig;
this.fileStorageService = fileStorageService;
this.staticFileStorageService = staticFileStorageService;
this.scriptServer = scriptServer;
}
/**
@ -342,9 +348,17 @@ public class FileReleaseTaskService extends BaseWorkspaceService<FileReleaseTask
}
String releasePath = model.getReleasePath();
if (StrUtil.isNotEmpty(model.getBeforeScript())) {
String beforeScript = model.getBeforeScript();
if (StrUtil.isNotEmpty(beforeScript)) {
logRecorder.system("开始执行上传前命令");
this.runNodeScript(model.getBeforeScript(), item, logRecorder, modelId, environmentMapBuilder, releasePath);
if (StrUtil.startWith(beforeScript, ServerConst.REF_SCRIPT)) {
String scriptId = StrUtil.removePrefix(beforeScript, ServerConst.REF_SCRIPT);
ScriptModel keyAndGlobal = scriptServer.getByKey(scriptId);
Assert.notNull(keyAndGlobal, "请选择正确的脚本");
beforeScript = keyAndGlobal.getContext();
logRecorder.system("引入脚本内容:{}[{}]", keyAndGlobal.getName(), scriptId);
}
this.runNodeScript(beforeScript, item, logRecorder, modelId, environmentMapBuilder, releasePath);
}
logRecorder.system("{} start file upload", item.getName());
// 上传文件
@ -374,9 +388,17 @@ public class FileReleaseTaskService extends BaseWorkspaceService<FileReleaseTask
}
logRecorder.system("{} file upload done", item.getName());
if (StrUtil.isNotEmpty(model.getAfterScript())) {
String afterScript = model.getAfterScript();
if (StrUtil.isNotEmpty(afterScript)) {
logRecorder.system("开始执行上传后命令");
this.runNodeScript(model.getAfterScript(), item, logRecorder, modelId, environmentMapBuilder, releasePath);
if (StrUtil.startWith(afterScript, ServerConst.REF_SCRIPT)) {
String scriptId = StrUtil.removePrefix(afterScript, ServerConst.REF_SCRIPT);
ScriptModel keyAndGlobal = scriptServer.getByKey(scriptId);
Assert.notNull(keyAndGlobal, "请选择正确的脚本");
afterScript = keyAndGlobal.getContext();
logRecorder.system("引入脚本内容:{}[{}]", keyAndGlobal.getName(), scriptId);
}
this.runNodeScript(afterScript, item, logRecorder, modelId, environmentMapBuilder, releasePath);
}
this.updateStatus(taskId, modelId, 2, "发布成功");
} catch (Exception e) {
@ -471,9 +493,17 @@ public class FileReleaseTaskService extends BaseWorkspaceService<FileReleaseTask
session = sshService.getSessionByModel(machineSshModel);
Map<String, String> environment = environmentMapBuilder.environment();
environmentMapBuilder.eachStr(logRecorder::system);
if (StrUtil.isNotEmpty(model.getBeforeScript())) {
String beforeScript = model.getBeforeScript();
if (StrUtil.isNotEmpty(beforeScript)) {
logRecorder.system("开始执行上传前命令");
JschUtils.execCallbackLine(session, charset, timeout, model.getBeforeScript(), StrUtil.EMPTY, environment, logRecorder::info);
if (StrUtil.startWith(beforeScript, ServerConst.REF_SCRIPT)) {
String scriptId = StrUtil.removePrefix(beforeScript, ServerConst.REF_SCRIPT);
ScriptModel keyAndGlobal = scriptServer.getByKey(scriptId);
Assert.notNull(keyAndGlobal, "请选择正确的脚本");
beforeScript = keyAndGlobal.getContext();
logRecorder.system("引入脚本内容:{}[{}]", keyAndGlobal.getName(), scriptId);
}
JschUtils.execCallbackLine(session, charset, timeout, beforeScript, StrUtil.EMPTY, environment, logRecorder::info);
}
logRecorder.system("{} start ftp upload", item.getName());
@ -485,9 +515,17 @@ public class FileReleaseTaskService extends BaseWorkspaceService<FileReleaseTask
sftp.syncUpload(storageSaveFile, releasePath);
logRecorder.system("{} ftp upload done", item.getName());
if (StrUtil.isNotEmpty(model.getAfterScript())) {
String afterScript = model.getAfterScript();
if (StrUtil.isNotEmpty(afterScript)) {
logRecorder.system("开始执行上传后命令");
JschUtils.execCallbackLine(session, charset, timeout, model.getAfterScript(), StrUtil.EMPTY, environment, logRecorder::info);
if (StrUtil.startWith(afterScript, ServerConst.REF_SCRIPT)) {
String scriptId = StrUtil.removePrefix(afterScript, ServerConst.REF_SCRIPT);
ScriptModel keyAndGlobal = scriptServer.getByKey(scriptId);
Assert.notNull(keyAndGlobal, "请选择正确的脚本");
afterScript = keyAndGlobal.getContext();
logRecorder.system("引入脚本内容:{}[{}]", keyAndGlobal.getName(), scriptId);
}
JschUtils.execCallbackLine(session, charset, timeout, afterScript, StrUtil.EMPTY, environment, logRecorder::info);
}
this.updateStatus(taskId, modelId, 2, "发布成功");
} catch (Exception e) {

View File

@ -151,10 +151,22 @@
}"
>
<template #tool_before>
<a-tag
><b>{{ $tl('c.content') }}</b
>{{ $tl('p.execute') }}</a-tag
>
<a-space>
<a-tag>
<b>{{ $tl('c.content') }}</b>
{{ $tl('p.execute') }}
</a-tag>
<a-button
type="link"
@click="
() => {
chooseScriptVisible = 1
}
"
>
脚本模板
</a-button>
</a-space>
</template>
</code-editor>
</a-tab-pane>
@ -168,15 +180,27 @@
}"
>
<template #tool_before>
<a-tag>{{ $tl('p.afterUploadExecute') }}</a-tag></template
>
<a-space>
<a-tag>{{ $tl('p.afterUploadExecute') }}</a-tag>
<a-button
type="link"
@click="
() => {
chooseScriptVisible = 2
}
"
>
脚本模板
</a-button>
</a-space>
</template>
</code-editor>
</a-tab-pane>
</a-tabs>
</a-form-item-rest>
</a-form-item>
</a-form>
<!-- 配置授权目录 -->
<a-modal
v-model:value="configDir"
destroy-on-close
@ -199,6 +223,76 @@
"
></whiteList>
</a-modal>
<!-- 选择脚本 -->
<a-drawer
destroy-on-close
title="选择脚本"
placement="right"
:open="chooseScriptVisible != 0"
width="70vw"
:z-index="1009"
:footer-style="{ textAlign: 'right' }"
@close="
() => {
chooseScriptVisible = 0
}
"
>
<scriptPage
v-if="chooseScriptVisible"
ref="scriptPage"
choose="radio"
:choose-val="
chooseScriptVisible === 1
? temp.beforeScript?.indexOf('$ref.script.') !== -1
? temp.beforeScript?.replace('$ref.script.')
: ''
: temp.afterScript?.indexOf('$ref.script.') !== -1
? temp.afterScript?.replace('$ref.script.')
: ''
"
mode="choose"
@confirm="
(id) => {
if (chooseScriptVisible === 1) {
temp = { ...temp, beforeScript: '$ref.script.' + id }
} else if (chooseScriptVisible === 2) {
temp = { ...temp, afterScript: '$ref.script.' + id }
}
chooseScriptVisible = 0
}
"
@cancel="
() => {
chooseScriptVisible = 0
}
"
></scriptPage>
<template #footer>
<a-space>
<a-button
@click="
() => {
chooseScriptVisible = false
}
"
>
取消
</a-button>
<a-button
type="primary"
@click="
() => {
$refs['scriptPage'].handerConfirm()
}
"
>
确认
</a-button>
</a-space>
</template>
</a-drawer>
</div>
</template>
@ -208,10 +302,12 @@ import { getDispatchWhiteList } from '@/api/dispatch'
import { getNodeListAll } from '@/api/node'
import codeEditor from '@/components/codeEditor'
import whiteList from '@/pages/dispatch/white-list.vue'
import scriptPage from '@/pages/script/script-list.vue'
export default {
components: {
codeEditor,
whiteList
whiteList,
scriptPage
},
emits: ['commit'],
data() {
@ -233,7 +329,8 @@ export default {
accessList: [],
nodeList: [],
configDir: false,
scriptTabKey: 'before'
scriptTabKey: 'before',
chooseScriptVisible: 0
}
},
created() {