项目新增自启动配置、构建新增 webhook、仓库添加验证

This commit is contained in:
bwcx_jzy 2021-12-10 13:30:40 +08:00
parent 4d3b3a7d05
commit a88fc0dcc4
29 changed files with 598 additions and 442 deletions

View File

@ -15,6 +15,8 @@
9. 【server】发布命令SSH发布命令、本地命令支持变量替换`#{BUILD_ID}`、`#{BUILD_NAME}`、`#{BUILD_RESULT_FILE}`、`#{BUILD_NUMBER_ID}`
10. 【server】新增自动备份全量数据配置 `db.autoBackupIntervalDay` 默认一天备份一次,执行备份时间 凌晨0点或者中午12点
11. 【agent】项目的 webhook 新增项目启动成功后通知,并且参数新增 `type` 指包括:`beforeStop`,`start`,`stop`,`beforeRestart`
12. 【agent】项目新增自启动配置项,在 agent 启动时候检查对应项目是否启动,未启动执行启动逻辑
13. 【server】构建新增 webhook实时通知构建进度
### 解决BUG、优化功能
@ -50,7 +52,7 @@
>
> 7: 此次升级启动耗时可能需要2分钟以上耗时根据数据量来决定请耐心等待和观察控制台日志输出
>
> 8: 一个节点不要被多个服务端绑定
> 8: 一个节点建议不要被多个服务端绑定(可能出现数据工作空间错乱情况)
------
# 2.7.3

View File

@ -262,14 +262,9 @@ public abstract class AbstractProjectCommander {
*/
public String restart(NodeProjectInfoModel nodeProjectInfoModel, NodeProjectInfoModel.JavaCopyItem javaCopyItem) throws Exception {
this.asyncWebHooks(nodeProjectInfoModel, javaCopyItem, "beforeRestart");
if (javaCopyItem == null) {
if (isRun(nodeProjectInfoModel.getId())) {
stop(nodeProjectInfoModel, null);
}
} else {
if (isRun(javaCopyItem.getTagId())) {
stop(nodeProjectInfoModel, javaCopyItem);
}
boolean run = this.isRun(nodeProjectInfoModel, javaCopyItem);
if (run) {
stop(nodeProjectInfoModel, javaCopyItem);
}
return start(nodeProjectInfoModel, javaCopyItem);
}
@ -367,33 +362,23 @@ public abstract class AbstractProjectCommander {
return "ok";
}
public String status(NodeProjectInfoModel nodeProjectInfoModel, NodeProjectInfoModel.JavaCopyItem javaCopyItem) {
String tag = javaCopyItem == null ? nodeProjectInfoModel.getId() : javaCopyItem.getTagId();
return this.status(tag);
}
/**
* 查看状态
*
* @param tag 运行标识
* @return 查询结果
* @throws Exception 异常
*/
public String status(String tag) throws Exception {
boolean disableVirtualMachine = AgentExtConfigBean.getInstance().isDisableVirtualMachine();
if (disableVirtualMachine) {
String jpsStatus = getJpsStatus(tag);
if (StrUtil.equals(AbstractProjectCommander.STOP_TAG, jpsStatus) && SystemUtil.getOsInfo().isLinux()) {
return getLinuxPsStatus(tag);
}
return jpsStatus;
} else {
Integer pid = JvmUtil.getPidByTag(tag);
if (pid == null) {
String jpsStatus = getJpsStatus(tag);
if (StrUtil.equals(AbstractProjectCommander.STOP_TAG, jpsStatus) && SystemUtil.getOsInfo().isLinux()) {
return getLinuxPsStatus(tag);
}
return jpsStatus;
}
return StrUtil.format("{}:{}", AbstractProjectCommander.RUNNING_TAG, pid);
protected String status(String tag) {
String jpsStatus = getJpsStatus(tag);
if (StrUtil.equals(AbstractProjectCommander.STOP_TAG, jpsStatus) && SystemUtil.getOsInfo().isLinux()) {
return getLinuxPsStatus(tag);
}
return jpsStatus;
}
/**
@ -403,15 +388,11 @@ public abstract class AbstractProjectCommander {
* @return 运行标识
*/
private String getJpsStatus(String tag) {
String execSystemCommand = CommandUtil.execSystemCommand("jps -mv");
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[0]);
}
Integer pid = JvmUtil.getPidByTag(tag);
if (pid == null || pid <= 0) {
return AbstractProjectCommander.STOP_TAG;
}
return AbstractProjectCommander.STOP_TAG;
return StrUtil.format("{}:{}", AbstractProjectCommander.RUNNING_TAG, pid);
}
@ -562,12 +543,23 @@ public abstract class AbstractProjectCommander {
/**
* 是否正在运行
*
* @param tag id
* @param nodeProjectInfoModel 项目
* @return true 正在运行
* @throws Exception 异常
*/
public boolean isRun(String tag) throws Exception {
String result = status(tag);
public boolean isRun(NodeProjectInfoModel nodeProjectInfoModel, NodeProjectInfoModel.JavaCopyItem javaCopyItem) {
String tag = javaCopyItem == null ? nodeProjectInfoModel.getId() : javaCopyItem.getTagId();
String result = this.status(tag);
return result.contains(AbstractProjectCommander.RUNNING_TAG);
}
/**
* 是否正在运行
*
* @param tag 运行标识
* @return true 正在运行
*/
private boolean isRun(String tag) {
String result = this.status(tag);
return result.contains(AbstractProjectCommander.RUNNING_TAG);
}

View File

@ -26,10 +26,8 @@ import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.PatternPool;
import cn.hutool.core.lang.RegexPool;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import cn.jiangzeyin.common.DefaultSystemLog;
import cn.jiangzeyin.common.JsonMessage;
@ -79,9 +77,8 @@ public class ManageEditProjectController extends BaseAgentController {
* @param projectInfo 项目实体
* @param whitelistDirectory 白名单
* @param previewData 预检查数据
* @return null 检查正常
*/
private String checkParameter(NodeProjectInfoModel projectInfo, String whitelistDirectory, boolean previewData) {
private void checkParameter(NodeProjectInfoModel projectInfo, String whitelistDirectory, boolean previewData) {
String id = projectInfo.getId();
Assert.state(!StrUtil.isEmptyOrUndefined(id), "项目id不能为空");
Assert.state(StringUtil.isGeneral(id, 2, 20), "项目id 长度范围2-20英文字母 、数字和下划线)");
@ -117,7 +114,7 @@ public class ManageEditProjectController extends BaseAgentController {
if (outGivingProject) {
whitelistDirectoryService.addProjectWhiteList(whitelistDirectory);
} else {
return JsonMessage.getString(401, "请选择正确的项目路径,或者还没有配置白名单");
throw new IllegalArgumentException("请选择正确的项目路径,或者还没有配置白名单");
}
}
String logPath = projectInfo.getLogPath();
@ -126,7 +123,7 @@ public class ManageEditProjectController extends BaseAgentController {
if (outGivingProject) {
whitelistDirectoryService.addProjectWhiteList(logPath);
} else {
return JsonMessage.getString(401, "请填写的项目日志存储路径,或者还没有配置白名单");
throw new IllegalArgumentException("请填写的项目日志存储路径,或者还没有配置白名单");
}
}
}
@ -162,7 +159,6 @@ public class ManageEditProjectController extends BaseAgentController {
} else {
projectInfo.setJavaCopyItemList(null);
}
return null;
}
@ -173,10 +169,8 @@ public class ManageEditProjectController extends BaseAgentController {
boolean previewData = Convert.toBool(strPreviewData, false);
String whitelistDirectory = projectInfo.getWhitelistDirectory();
//
String error = checkParameter(projectInfo, whitelistDirectory, previewData);
if (error != null) {
return error;
}
this.checkParameter(projectInfo, whitelistDirectory, previewData);
String id = projectInfo.getId();
//
String allLib = projectInfo.allLib();
@ -205,7 +199,7 @@ public class ManageEditProjectController extends BaseAgentController {
}
//
String token = projectInfo.getToken();
if (StrUtil.isNotEmpty(token) && !ReUtil.isMatch(PatternPool.URL_HTTP, token)) {
if (StrUtil.isNotEmpty(token)) {
Validator.validateMatchRegex(RegexPool.URL_HTTP, token, "WebHooks 地址不合法");
}
// 判断空格
@ -234,7 +228,7 @@ public class ManageEditProjectController extends BaseAgentController {
this.checkPath(projectInfo);
if (exits == null) {
// 检查运行中的tag 是否被占用
Assert.state(!AbstractProjectCommander.getInstance().isRun(projectInfo.getId()), "当前项目id已经被正在运行的程序占用");
Assert.state(!AbstractProjectCommander.getInstance().isRun(projectInfo, null), "当前项目id已经被正在运行的程序占用");
if (previewData) {
// 预检查数据
return JsonMessage.getString(200, "检查通过");
@ -251,6 +245,7 @@ public class ManageEditProjectController extends BaseAgentController {
exits.setLogPath(projectInfo.getLogPath());
exits.setName(projectInfo.getName());
// exits.setGroup(projectInfo.getGroup());
exits.setAutoStart(projectInfo.getAutoStart());
exits.setMainClass(projectInfo.getMainClass());
exits.setLib(projectInfo.getLib());
exits.setJvm(projectInfo.getJvm());
@ -348,13 +343,15 @@ public class ManageEditProjectController extends BaseAgentController {
Assert.notNull(nodeProjectInfoModel, "项目不存在");
try {
NodeProjectInfoModel.JavaCopyItem copyItem = nodeProjectInfoModel.findCopyItem(copyId);
if (copyItem == null) {
// 运行判断
Assert.state(!nodeProjectInfoModel.tryGetStatus(), "不能删除正在运行的项目");
boolean run = AbstractProjectCommander.getInstance().isRun(nodeProjectInfoModel, null);
Assert.state(!run, "不能删除正在运行的项目");
projectInfoService.deleteItem(nodeProjectInfoModel.getId());
} else {
Assert.state(!copyItem.tryGetStatus(), "不能删除正在运行的项目副本");
boolean run = AbstractProjectCommander.getInstance().isRun(nodeProjectInfoModel, copyItem);
Assert.state(!run, "不能删除正在运行的项目副本");
boolean removeCopyItem = nodeProjectInfoModel.removeCopyItem(copyId);
Assert.state(removeCopyItem, "删除对应副本集不存在");
projectInfoService.updateItem(nodeProjectInfoModel);

View File

@ -219,9 +219,8 @@ public class ProjectFileControl extends BaseAgentController {
if (FileUtil.clean(file)) {
return JsonMessage.getString(200, "清除成功");
}
if (pim.tryGetStatus()) {
return JsonMessage.getString(501, "文件被占用,请先停止项目");
}
boolean run = AbstractProjectCommander.getInstance().isRun(pim, null);
Assert.state(!run, "文件被占用,请先停止项目");
return JsonMessage.getString(500, "删除失败:" + file.getAbsolutePath());
} else {
// 删除文件

View File

@ -22,8 +22,6 @@
*/
package io.jpom.controller.manage;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.jiangzeyin.common.DefaultSystemLog;
import cn.jiangzeyin.common.JsonMessage;
import com.alibaba.fastjson.JSONArray;
@ -32,6 +30,7 @@ import io.jpom.common.BaseAgentController;
import io.jpom.common.commander.AbstractProjectCommander;
import io.jpom.model.data.NodeProjectInfoModel;
import org.springframework.http.MediaType;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@ -66,52 +65,17 @@ public class ProjectListController extends BaseAgentController {
return JsonMessage.getString(200, "", nodeProjectInfoModel);
}
// /**
// * 获取所有的分组
// *
// * @return array
// */
// @RequestMapping(value = "getProjectGroup", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
// public String getProjectGroup() {
// HashSet<String> strings = projectInfoService.getAllGroup();
// return JsonMessage.getString(200, "", strings);
// }
/**
* 程序项目信息
*
* @param notStatus 不包含运行状态
* @return json
*/
@RequestMapping(value = "getProjectInfo", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public String getProjectInfo(String notStatus) {
public String getProjectInfo() {
try {
boolean status = StrUtil.isEmpty(notStatus);
// 查询数据
List<NodeProjectInfoModel> nodeProjectInfoModels = projectInfoService.list();
// 转换为数据
JSONArray array = new JSONArray();
for (NodeProjectInfoModel nodeProjectInfoModel : nodeProjectInfoModels) {
// if (StrUtil.isNotEmpty(group) && !group.equals(nodeProjectInfoModel.getGroup())) {
// continue;
// }
JSONObject object = nodeProjectInfoModel.toJson();
if (status) {
object.put("status", nodeProjectInfoModel.tryGetStatus());
}
array.add(object);
}
array.sort((oo1, oo2) -> {
JSONObject o1 = (JSONObject) oo1;
JSONObject o2 = (JSONObject) oo2;
String group1 = o1.getString("group");
String group2 = o2.getString("group");
if (group1 == null || group2 == null) {
return -1;
}
return group1.compareTo(group2);
});
return JsonMessage.getString(200, "查询成功!", array);
return JsonMessage.getString(200, "查询成功!", nodeProjectInfoModels);
} catch (Exception e) {
DefaultSystemLog.getLog().error(e.getMessage(), e);
return JsonMessage.getString(500, "查询异常:" + e.getMessage());
@ -124,20 +88,17 @@ public class ProjectListController extends BaseAgentController {
@RequestMapping(value = "project_copy_list", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public String projectCopyList(String id) {
NodeProjectInfoModel nodeProjectInfoModel = projectInfoService.getItem(id);
if (nodeProjectInfoModel == null) {
return JsonMessage.getString(404, "没有对应项目");
}
Assert.notNull(nodeProjectInfoModel, "没有对应项目");
List<NodeProjectInfoModel.JavaCopyItem> javaCopyItemList = nodeProjectInfoModel.getJavaCopyItemList();
if (CollUtil.isEmpty(javaCopyItemList)) {
return JsonMessage.getString(404, "对应项目没有副本集");
}
Assert.notEmpty(javaCopyItemList, "对应项目没有副本集");
JSONArray array = new JSONArray();
for (NodeProjectInfoModel.JavaCopyItem javaCopyItem : javaCopyItemList) {
JSONObject object = javaCopyItem.toJson();
object.put("status", javaCopyItem.tryGetStatus());
boolean run = AbstractProjectCommander.getInstance().isRun(nodeProjectInfoModel, javaCopyItem);
object.put("status", run);
array.add(object);
}
return JsonMessage.getString(200, "", array);
}
}

View File

@ -35,8 +35,8 @@ import io.jpom.common.commander.AbstractProjectCommander;
import io.jpom.model.data.NodeProjectInfoModel;
import io.jpom.service.manage.ConsoleService;
import io.jpom.socket.ConsoleCommandOp;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@ -53,161 +53,154 @@ import java.util.List;
@RequestMapping(value = "/manage/")
public class ProjectStatusController extends BaseAgentController {
private final ConsoleService consoleService;
private final ConsoleService consoleService;
public ProjectStatusController(ConsoleService consoleService) {
this.consoleService = consoleService;
}
public ProjectStatusController(ConsoleService consoleService) {
this.consoleService = consoleService;
}
/**
* 获取项目的进程id
*
* @param id 项目id
* @return json
*/
@RequestMapping(value = "getProjectStatus", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public String getProjectStatus(@ValidatorConfig(@ValidatorItem(value = ValidatorRule.NOT_BLANK, msg = "项目id 不正确")) String id, String getCopy) {
NodeProjectInfoModel nodeProjectInfoModel = tryGetProjectInfoModel();
if (nodeProjectInfoModel == null) {
return JsonMessage.getString(HttpStatus.NOT_FOUND.value(), "项目id不存在");
}
int pid = 0;
try {
pid = AbstractProjectCommander.getInstance().getPid(id);
} catch (Exception e) {
DefaultSystemLog.getLog().error("获取项目pid 失败", e);
}
JSONObject jsonObject = new JSONObject();
jsonObject.put("pId", pid);
//
if (StrUtil.isNotEmpty(getCopy)) {
List<NodeProjectInfoModel.JavaCopyItem> javaCopyItemList = nodeProjectInfoModel.getJavaCopyItemList();
JSONArray copys = new JSONArray();
if (javaCopyItemList != null) {
for (NodeProjectInfoModel.JavaCopyItem javaCopyItem : javaCopyItemList) {
JSONObject jsonObject1 = new JSONObject();
jsonObject1.put("copyId", javaCopyItem.getId());
jsonObject1.put("status", javaCopyItem.tryGetStatus());
copys.add(jsonObject1);
}
}
jsonObject.put("copys", copys);
}
return JsonMessage.getString(200, "", jsonObject);
}
/**
* 获取项目的进程id
*
* @param id 项目id
* @return json
*/
@RequestMapping(value = "getProjectStatus", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public String getProjectStatus(@ValidatorConfig(@ValidatorItem(value = ValidatorRule.NOT_BLANK, msg = "项目id 不正确")) String id, String getCopy) {
NodeProjectInfoModel nodeProjectInfoModel = tryGetProjectInfoModel();
Assert.notNull(nodeProjectInfoModel, "项目id不存在");
int pid = 0;
try {
pid = AbstractProjectCommander.getInstance().getPid(id);
} catch (Exception e) {
DefaultSystemLog.getLog().error("获取项目pid 失败", e);
}
JSONObject jsonObject = new JSONObject();
jsonObject.put("pId", pid);
//
if (StrUtil.isNotEmpty(getCopy)) {
List<NodeProjectInfoModel.JavaCopyItem> javaCopyItemList = nodeProjectInfoModel.getJavaCopyItemList();
JSONArray copys = new JSONArray();
if (javaCopyItemList != null) {
for (NodeProjectInfoModel.JavaCopyItem javaCopyItem : javaCopyItemList) {
JSONObject jsonObject1 = new JSONObject();
jsonObject1.put("copyId", javaCopyItem.getId());
boolean run = AbstractProjectCommander.getInstance().isRun(nodeProjectInfoModel, javaCopyItem);
jsonObject1.put("status", run);
copys.add(jsonObject1);
}
}
jsonObject.put("copys", copys);
}
return JsonMessage.getString(200, "", jsonObject);
}
/**
* 获取项目的运行端口
*
* @param ids ids
* @return obj
*/
@RequestMapping(value = "getProjectPort", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public String getProjectPort(String ids) {
if (StrUtil.isEmpty(ids)) {
return JsonMessage.getString(400, "");
}
JSONArray jsonArray = JSONArray.parseArray(ids);
JSONObject jsonObject = new JSONObject();
JSONObject itemObj;
for (Object object : jsonArray) {
String item = object.toString();
int pid;
try {
pid = AbstractProjectCommander.getInstance().getPid(item);
} catch (Exception e) {
DefaultSystemLog.getLog().error("获取端口错误", e);
continue;
}
if (pid <= 0) {
continue;
}
itemObj = new JSONObject();
String port = AbstractProjectCommander.getInstance().getMainPort(pid);
itemObj.put("port", port);
itemObj.put("pid", pid);
jsonObject.put(item, itemObj);
}
return JsonMessage.getString(200, "", jsonObject);
}
/**
* 获取项目的运行端口
*
* @param ids ids
* @return obj
*/
@RequestMapping(value = "getProjectPort", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public String getProjectPort(String ids) {
Assert.hasText(ids, "没有要获取的信息");
JSONArray jsonArray = JSONArray.parseArray(ids);
JSONObject jsonObject = new JSONObject();
JSONObject itemObj;
for (Object object : jsonArray) {
String item = object.toString();
int pid;
try {
pid = AbstractProjectCommander.getInstance().getPid(item);
} catch (Exception e) {
DefaultSystemLog.getLog().error("获取端口错误", e);
continue;
}
if (pid <= 0) {
continue;
}
itemObj = new JSONObject();
String port = AbstractProjectCommander.getInstance().getMainPort(pid);
itemObj.put("port", port);
itemObj.put("pid", pid);
jsonObject.put(item, itemObj);
}
return JsonMessage.getString(200, "", jsonObject);
}
/**
* 获取项目的运行端口
*
* @param id 项目id
* @param copyIds 副本 ids ["aa","ss"]
* @return obj
*/
@RequestMapping(value = "getProjectCopyPort", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public String getProjectPort(String id, String copyIds) {
if (StrUtil.isEmpty(copyIds) || StrUtil.isEmpty(id)) {
return JsonMessage.getString(400, "");
}
NodeProjectInfoModel nodeProjectInfoModel = getProjectInfoModel();
/**
* 获取项目的运行端口
*
* @param id 项目id
* @param copyIds 副本 ids ["aa","ss"]
* @return obj
*/
@RequestMapping(value = "getProjectCopyPort", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public String getProjectPort(String id, String copyIds) {
if (StrUtil.isEmpty(copyIds) || StrUtil.isEmpty(id)) {
return JsonMessage.getString(400, "");
}
NodeProjectInfoModel nodeProjectInfoModel = getProjectInfoModel();
JSONArray jsonArray = JSONArray.parseArray(copyIds);
JSONObject jsonObject = new JSONObject();
JSONObject itemObj;
for (Object object : jsonArray) {
String item = object.toString();
NodeProjectInfoModel.JavaCopyItem copyItem = nodeProjectInfoModel.findCopyItem(item);
int pid;
try {
pid = AbstractProjectCommander.getInstance().getPid(copyItem.getTagId());
if (pid <= 0) {
continue;
}
} catch (Exception e) {
DefaultSystemLog.getLog().error("获取端口错误", e);
continue;
}
itemObj = new JSONObject();
String port = AbstractProjectCommander.getInstance().getMainPort(pid);
itemObj.put("port", port);
itemObj.put("pid", pid);
jsonObject.put(item, itemObj);
}
return JsonMessage.getString(200, "", jsonObject);
}
JSONArray jsonArray = JSONArray.parseArray(copyIds);
JSONObject jsonObject = new JSONObject();
JSONObject itemObj;
for (Object object : jsonArray) {
String item = object.toString();
NodeProjectInfoModel.JavaCopyItem copyItem = nodeProjectInfoModel.findCopyItem(item);
int pid;
try {
pid = AbstractProjectCommander.getInstance().getPid(copyItem.getTagId());
if (pid <= 0) {
continue;
}
} catch (Exception e) {
DefaultSystemLog.getLog().error("获取端口错误", e);
continue;
}
itemObj = new JSONObject();
String port = AbstractProjectCommander.getInstance().getMainPort(pid);
itemObj.put("port", port);
itemObj.put("pid", pid);
jsonObject.put(item, itemObj);
}
return JsonMessage.getString(200, "", jsonObject);
}
@RequestMapping(value = "restart", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public String restart(@ValidatorConfig(@ValidatorItem(value = ValidatorRule.NOT_BLANK, msg = "项目id 不正确")) String id, String copyId) {
NodeProjectInfoModel item = projectInfoService.getItem(id);
if (item == null) {
return JsonMessage.getString(405, "没有找到对应的项目");
}
NodeProjectInfoModel.JavaCopyItem copyItem = item.findCopyItem(copyId);
String tagId = copyItem == null ? item.getId() : copyItem.getTagId();
String result;
try {
result = consoleService.execCommand(ConsoleCommandOp.restart, item, copyItem);
boolean status = AbstractProjectCommander.getInstance().isRun(tagId);
if (status) {
return JsonMessage.getString(200, result);
}
return JsonMessage.getString(201, "重启项目失败:" + result);
} catch (Exception e) {
DefaultSystemLog.getLog().error("获取项目pid 失败", e);
result = "error:" + e.getMessage();
return JsonMessage.getString(500, "重启项目异常:" + result);
}
}
@RequestMapping(value = "restart", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public String restart(@ValidatorConfig(@ValidatorItem(value = ValidatorRule.NOT_BLANK, msg = "项目id 不正确")) String id, String copyId) {
NodeProjectInfoModel item = projectInfoService.getItem(id);
Assert.notNull(item, "没有找到对应的项目");
NodeProjectInfoModel.JavaCopyItem copyItem = item.findCopyItem(copyId);
String result;
try {
result = consoleService.execCommand(ConsoleCommandOp.restart, item, copyItem);
boolean status = AbstractProjectCommander.getInstance().isRun(item, copyItem);
if (status) {
return JsonMessage.getString(200, result);
}
return JsonMessage.getString(201, "重启项目失败:" + result);
} catch (Exception e) {
DefaultSystemLog.getLog().error("获取项目pid 失败", e);
result = "error:" + e.getMessage();
return JsonMessage.getString(500, "重启项目异常:" + result);
}
}
@RequestMapping(value = "stop", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public String stop(@ValidatorConfig(@ValidatorItem(value = ValidatorRule.NOT_BLANK, msg = "项目id 不正确")) String id, String copyId) {
NodeProjectInfoModel item = projectInfoService.getItem(id);
if (item == null) {
return JsonMessage.getString(405, "没有找到对应的项目");
}
Assert.notNull(item, "没有找到对应的项目");
NodeProjectInfoModel.JavaCopyItem copyItem = item.findCopyItem(copyId);
String tagId = copyItem == null ? item.getId() : copyItem.getTagId();
String result;
try {
result = consoleService.execCommand(ConsoleCommandOp.stop, item, copyItem);
boolean status = AbstractProjectCommander.getInstance().isRun(tagId);
boolean status = AbstractProjectCommander.getInstance().isRun(item, copyItem);
if (!status) {
return JsonMessage.getString(200, result);
}
@ -223,15 +216,12 @@ public class ProjectStatusController extends BaseAgentController {
@RequestMapping(value = "start", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public String start(@ValidatorConfig(@ValidatorItem(value = ValidatorRule.NOT_BLANK, msg = "项目id 不正确")) String id, String copyId) {
NodeProjectInfoModel item = projectInfoService.getItem(id);
if (item == null) {
return JsonMessage.getString(405, "没有找到对应的项目");
}
Assert.notNull(item, "没有找到对应的项目");
NodeProjectInfoModel.JavaCopyItem copyItem = item.findCopyItem(copyId);
String tagId = copyItem == null ? item.getId() : copyItem.getTagId();
String result;
try {
result = consoleService.execCommand(ConsoleCommandOp.start, item, copyItem);
boolean status = AbstractProjectCommander.getInstance().isRun(tagId);
boolean status = AbstractProjectCommander.getInstance().isRun(item, copyItem);
if (status) {
return JsonMessage.getString(200, result);
}

View File

@ -27,10 +27,8 @@ import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HtmlUtil;
import cn.jiangzeyin.common.DefaultSystemLog;
import cn.jiangzeyin.common.request.XssFilter;
import cn.jiangzeyin.common.spring.SpringUtil;
import io.jpom.common.commander.AbstractProjectCommander;
import io.jpom.model.BaseJsonModel;
import io.jpom.model.BaseModel;
import io.jpom.model.RunMode;
@ -50,7 +48,7 @@ import java.util.stream.Collectors;
* @author jiangzeyin
*/
public class NodeProjectInfoModel extends BaseModel {
// /**
// /**
// * 分组
// */
// private String group;
@ -115,6 +113,11 @@ public class NodeProjectInfoModel extends BaseModel {
private String workspaceId;
/**
* 项目自动启动
*/
private Boolean autoStart;
public String getWorkspaceId() {
return workspaceId;
}
@ -177,21 +180,6 @@ public class NodeProjectInfoModel extends BaseModel {
this.modifyUser = modifyUser;
}
/**
* 项目是否正在运行
*
* @return true 正在运行
*/
public boolean tryGetStatus() {
try {
status = AbstractProjectCommander.getInstance().isRun(getId());
} catch (Exception e) {
DefaultSystemLog.getLog().error("检查项目状态错误", e);
status = false;
}
return status;
}
public void setStatus(boolean status) {
this.status = status;
}
@ -240,7 +228,16 @@ public class NodeProjectInfoModel extends BaseModel {
this.jvm = jvm;
}
}
//
public Boolean getAutoStart() {
return autoStart;
}
public void setAutoStart(Boolean autoStart) {
this.autoStart = autoStart;
}
//
// public String getGroup() {
// if (StrUtil.isEmpty(group)) {
// return "默认";
@ -565,20 +562,6 @@ public class NodeProjectInfoModel extends BaseModel {
this.modifyTime = modifyTime;
}
/**
* 项目是否正在运行
*
* @return true 正在运行
*/
public boolean tryGetStatus() {
try {
return AbstractProjectCommander.getInstance().isRun(getTagId());
} catch (Exception e) {
DefaultSystemLog.getLog().error("检查项目状态错误", e);
return false;
}
}
public String getParendId() {
return parendId;
}

View File

@ -38,57 +38,56 @@ import javax.annotation.Resource;
*/
@Service
public class ConsoleService {
@Resource
private ProjectInfoService projectInfoService;
@Resource
private ProjectInfoService projectInfoService;
/**
* 执行shell命令
*
* @param consoleCommandOp 执行的操作
* @param nodeProjectInfoModel 项目信息
* @param copyItem 副本信息
* @return 执行结果
* @throws Exception 异常
*/
public String execCommand(ConsoleCommandOp consoleCommandOp, NodeProjectInfoModel nodeProjectInfoModel, NodeProjectInfoModel.JavaCopyItem copyItem) throws Exception {
String result;
AbstractProjectCommander abstractProjectCommander = AbstractProjectCommander.getInstance();
// 执行命令
switch (consoleCommandOp) {
case restart:
result = abstractProjectCommander.restart(nodeProjectInfoModel, copyItem);
break;
case start:
result = abstractProjectCommander.start(nodeProjectInfoModel, copyItem);
break;
case stop:
result = abstractProjectCommander.stop(nodeProjectInfoModel, copyItem);
break;
case status: {
String tag = copyItem == null ? nodeProjectInfoModel.getId() : copyItem.getTagId();
result = abstractProjectCommander.status(tag);
break;
}
case top:
case showlog:
default:
throw new IllegalArgumentException(consoleCommandOp + " error");
}
// 通知日志刷新
if (consoleCommandOp == ConsoleCommandOp.start || consoleCommandOp == ConsoleCommandOp.restart) {
// 修改 run lib 使用情况
NodeProjectInfoModel modify = projectInfoService.getItem(nodeProjectInfoModel.getId());
//
if (copyItem != null) {
NodeProjectInfoModel.JavaCopyItem copyItem1 = modify.findCopyItem(copyItem.getId());
copyItem1.setModifyTime(DateUtil.now());
}
modify.setRunLibDesc(nodeProjectInfoModel.getUseLibDesc());
try {
projectInfoService.updateItem(modify);
} catch (Exception ignored) {
}
}
return result;
}
/**
* 执行shell命令
*
* @param consoleCommandOp 执行的操作
* @param nodeProjectInfoModel 项目信息
* @param copyItem 副本信息
* @return 执行结果
* @throws Exception 异常
*/
public String execCommand(ConsoleCommandOp consoleCommandOp, NodeProjectInfoModel nodeProjectInfoModel, NodeProjectInfoModel.JavaCopyItem copyItem) throws Exception {
String result;
AbstractProjectCommander abstractProjectCommander = AbstractProjectCommander.getInstance();
// 执行命令
switch (consoleCommandOp) {
case restart:
result = abstractProjectCommander.restart(nodeProjectInfoModel, copyItem);
break;
case start:
result = abstractProjectCommander.start(nodeProjectInfoModel, copyItem);
break;
case stop:
result = abstractProjectCommander.stop(nodeProjectInfoModel, copyItem);
break;
case status: {
result = abstractProjectCommander.status(nodeProjectInfoModel, copyItem);
break;
}
case top:
case showlog:
default:
throw new IllegalArgumentException(consoleCommandOp + " error");
}
// 通知日志刷新
if (consoleCommandOp == ConsoleCommandOp.start || consoleCommandOp == ConsoleCommandOp.restart) {
// 修改 run lib 使用情况
NodeProjectInfoModel modify = projectInfoService.getItem(nodeProjectInfoModel.getId());
//
if (copyItem != null) {
NodeProjectInfoModel.JavaCopyItem copyItem1 = modify.findCopyItem(copyItem.getId());
copyItem1.setModifyTime(DateUtil.now());
}
modify.setRunLibDesc(nodeProjectInfoModel.getUseLibDesc());
try {
projectInfoService.updateItem(modify);
} catch (Exception ignored) {
}
}
return result;
}
}

View File

@ -105,6 +105,8 @@ public class AutoRegSeverNode {
serverJson.put("updateTime", DateTime.now().toString());
}
JsonFileUtil.saveJson(file.getAbsolutePath(), serverJson);
} else {
DefaultSystemLog.getLog().error("自动注册插件端失败:{}", body);
}
}
}

View File

@ -0,0 +1,77 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 码之科技工作室
*
* 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.system.init;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.jiangzeyin.common.DefaultSystemLog;
import cn.jiangzeyin.common.PreLoadClass;
import cn.jiangzeyin.common.PreLoadMethod;
import cn.jiangzeyin.common.spring.SpringUtil;
import io.jpom.common.commander.AbstractProjectCommander;
import io.jpom.model.data.NodeProjectInfoModel;
import io.jpom.service.manage.ProjectInfoService;
import java.util.List;
import java.util.stream.Collectors;
/**
* 自动启动项目
*
* @author bwcx_jzy
* @since 2021/12/10
*/
@PreLoadClass
public class AutoStartProject {
@PreLoadMethod
private static void start() {
ProjectInfoService projectInfoService = SpringUtil.getBean(ProjectInfoService.class);
List<NodeProjectInfoModel> list = projectInfoService.list();
if (CollUtil.isEmpty(list)) {
return;
}
list = list.stream().filter(nodeProjectInfoModel -> nodeProjectInfoModel.getAutoStart() != null && nodeProjectInfoModel.getAutoStart()).collect(Collectors.toList());
List<NodeProjectInfoModel> finalList = list;
ThreadUtil.execute(() -> {
AbstractProjectCommander instance = AbstractProjectCommander.getInstance();
for (NodeProjectInfoModel nodeProjectInfoModel : finalList) {
try {
if (!instance.isRun(nodeProjectInfoModel, null)) {
instance.start(nodeProjectInfoModel, null);
}
List<NodeProjectInfoModel.JavaCopyItem> javaCopyItemList = nodeProjectInfoModel.getJavaCopyItemList();
if (javaCopyItemList != null) {
for (NodeProjectInfoModel.JavaCopyItem javaCopyItem : javaCopyItemList) {
if (!instance.isRun(nodeProjectInfoModel, javaCopyItem)) {
instance.start(nodeProjectInfoModel, javaCopyItem);
}
}
}
} catch (Exception e) {
DefaultSystemLog.getLog().warn("自动启动项目失败:{} {}", nodeProjectInfoModel.getId(), e.getMessage());
}
}
});
}
}

View File

@ -33,6 +33,8 @@ import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
import cn.jiangzeyin.common.DefaultSystemLog;
import cn.jiangzeyin.common.spring.SpringUtil;
import io.jpom.JpomApplication;
@ -393,20 +395,27 @@ public class BuildInfoManage extends BaseBuild implements Runnable {
for (Map.Entry<String, Supplier<Boolean>> stringSupplierEntry : suppliers.entrySet()) {
processName = stringSupplierEntry.getKey();
Supplier<Boolean> value = stringSupplierEntry.getValue();
//
this.asyncWebHooks(processName);
Boolean aBoolean = value.get();
if (!aBoolean) {
// 有条件结束构建流程
this.asyncWebHooks("stop", "process", processName);
break;
}
}
this.asyncWebHooks("success");
} catch (RuntimeException runtimeException) {
Throwable cause = runtimeException.getCause();
this.log("构建失败:" + processName, cause == null ? runtimeException : cause);
this.asyncWebHooks(processName, "error", runtimeException.getMessage());
} catch (Exception e) {
this.log("构建失败:" + processName, e);
this.asyncWebHooks(processName, "error", e.getMessage());
} finally {
BUILD_MANAGE_MAP.remove(buildInfoModel.getId());
BaseServerController.remove();
this.asyncWebHooks("done");
}
}
@ -442,4 +451,30 @@ public class BuildInfoManage extends BaseBuild implements Runnable {
log("process result " + waitFor);
return status[0];
}
/**
* 执行 webhooks 通知
*
* @param type 类型
* @param other 其他参数
*/
private void asyncWebHooks(String type, Object... other) {
String webhook = this.buildInfoModel.getWebhook();
if (StrUtil.isEmpty(webhook)) {
return;
}
ThreadUtil.execute(() -> {
try {
HttpRequest httpRequest = HttpUtil.createGet(webhook);
httpRequest.form("buildId", this.buildInfoModel.getId());
httpRequest.form("buildName", this.buildInfoModel.getName());
httpRequest.form("type", type, other);
String body = httpRequest.execute().body();
DefaultSystemLog.getLog().info(this.buildInfoModel.getName() + ":" + body);
} catch (Exception e) {
DefaultSystemLog.getLog().error("WebHooks 调用错误", e);
}
});
}
}

View File

@ -26,6 +26,7 @@ import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.core.util.ZipUtil;
import cn.hutool.crypto.SecureUtil;
import io.jpom.common.Const;
import io.jpom.model.data.BuildInfoModel;
import io.jpom.model.data.RepositoryModel;
@ -171,7 +172,13 @@ public class BuildUtil {
String rsaPath = StrUtil.removePrefix(repositoryModel.getRsaPrv(), URLUtil.FILE_URL_PREFIX);
rsaFile = FileUtil.file(rsaPath);
} else {
rsaFile = BuildUtil.getRepositoryRsaFile(repositoryModel.getId() + Const.ID_RSA);
if (StrUtil.isEmpty(repositoryModel.getId())) {
rsaFile = FileUtil.file(ConfigBean.getInstance().getTempPath(), Const.SSH_KEY, SecureUtil.sha1(repositoryModel.getGitUrl()) + Const.ID_RSA);
} else {
rsaFile = BuildUtil.getRepositoryRsaFile(repositoryModel.getId() + Const.ID_RSA);
}
// 写入
FileUtil.writeUtf8String(repositoryModel.getRsaPrv(), rsaFile);
}
Assert.state(FileUtil.isFile(rsaFile), "仓库密钥文件不存在或者异常,请检查后操作");
return rsaFile;

View File

@ -24,8 +24,9 @@ package io.jpom.controller.build;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.RegexPool;
import cn.hutool.core.lang.Tuple;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.StrUtil;
import cn.jiangzeyin.common.JsonMessage;
import cn.jiangzeyin.common.validator.ValidatorConfig;
@ -136,7 +137,7 @@ public class BuildInfoController extends BaseServerController {
@ValidatorConfig(@ValidatorItem(value = ValidatorRule.NOT_BLANK, msg = "构建产物目录不能为空,长度1-200", range = "1:200")) String resultDirFile,
@ValidatorConfig(@ValidatorItem(value = ValidatorRule.NOT_BLANK, msg = "构建命令不能为空")) String script,
@ValidatorItem(value = ValidatorRule.POSITIVE_INTEGER, msg = "发布方法不正确") int releaseMethod,
String branchName, String branchTagName, String group,
String branchName, String branchTagName, String webhook,
String extraData) {
// 根据 repositoryId 查询仓库信息
RepositoryModel repositoryModel = repositoryService.getByKey(repositoryId, getRequest());
@ -153,13 +154,15 @@ public class BuildInfoController extends BaseServerController {
Assert.state(!CommandUtil.checkContainsDel(script), "不能包含删除命令");
}
// 查询构建信息
BuildInfoModel buildInfoModel = buildInfoService.getByKey(id);
BuildInfoModel buildInfoModel = buildInfoService.getByKey(id, getRequest());
if (null == buildInfoModel) {
buildInfoModel = new BuildInfoModel();
buildInfoModel.setId(IdUtil.fastSimpleUUID());
}
// 设置参数
buildInfoModel.setGroup(group);
if (StrUtil.isNotEmpty(webhook)) {
Validator.validateMatchRegex(RegexPool.URL_HTTP, webhook, "WebHooks 地址不合法");
}
buildInfoModel.setWebhook(webhook);
buildInfoModel.setRepositoryId(repositoryId);
buildInfoModel.setName(name);
buildInfoModel.setBranchName(branchName);
@ -177,17 +180,12 @@ public class BuildInfoController extends BaseServerController {
// 验证发布方式 extraData 信息
if (releaseMethod1 == BuildReleaseMethod.Project) {
String formatProject = formatProject(jsonObject);
if (formatProject != null) {
return formatProject;
}
this.formatProject(jsonObject);
} else if (releaseMethod1 == BuildReleaseMethod.Ssh) {
this.formatSsh(jsonObject);
} else if (releaseMethod1 == BuildReleaseMethod.Outgiving) {
String releaseMethodDataId = jsonObject.getString("releaseMethodDataId_1");
if (StrUtil.isEmpty(releaseMethodDataId)) {
return JsonMessage.getString(405, "请选择分发项目");
}
Assert.hasText(releaseMethodDataId, "请选择分发项目");
jsonObject.put("releaseMethodDataId", releaseMethodDataId);
} else if (releaseMethod1 == BuildReleaseMethod.LocalCommand) {
this.formatLocalCommand(jsonObject);
@ -273,14 +271,12 @@ public class BuildInfoController extends BaseServerController {
* 当发布方式为项目的时候
*
* @param jsonObject 配置信息
* @return null 没有错误信息
*/
private String formatProject(JSONObject jsonObject) {
private void formatProject(JSONObject jsonObject) {
String releaseMethodDataId2Node = jsonObject.getString("releaseMethodDataId_2_node");
String releaseMethodDataId2Project = jsonObject.getString("releaseMethodDataId_2_project");
if (StrUtil.isEmpty(releaseMethodDataId2Node) || StrUtil.isEmpty(releaseMethodDataId2Project)) {
return JsonMessage.getString(405, "请选择节点和项目");
}
Assert.state(StrUtil.hasEmpty(releaseMethodDataId2Node, releaseMethodDataId2Project), "请选择节点和项目");
jsonObject.put("releaseMethodDataId", String.format("%s:%s", releaseMethodDataId2Node, releaseMethodDataId2Project));
//
String afterOpt = jsonObject.getString("afterOpt");
@ -290,7 +286,6 @@ public class BuildInfoController extends BaseServerController {
String clearOld = jsonObject.getString("clearOld");
jsonObject.put("afterOpt", afterOpt1.getCode());
jsonObject.put("clearOld", Convert.toBool(clearOld, false));
return null;
}
/**

View File

@ -103,30 +103,19 @@ public class RepositoryController extends BaseServerController {
@Feature(method = MethodFeature.EDIT)
public Object editRepository(RepositoryModel repositoryModelReq) {
this.checkInfo(repositoryModelReq);
if (StrUtil.isEmpty(repositoryModelReq.getId())) {
// insert data
repositoryService.insert(repositoryModelReq);
} else {
// update data
if (StrUtil.isEmpty(repositoryModelReq.getRsaPrv())) {
repositoryModelReq.setRsaPrv(null);
}
if (StrUtil.isEmpty(repositoryModelReq.getPassword())) {
repositoryModelReq.setPassword(null);
}
repositoryModelReq.setWorkspaceId(repositoryService.getCheckUserWorkspace(getRequest()));
repositoryService.updateById(repositoryModelReq);
}
// 检查 rsa 私钥
boolean andUpdateSshKey = checkAndUpdateSshKey(repositoryModelReq);
boolean andUpdateSshKey = this.checkAndUpdateSshKey(repositoryModelReq);
Assert.state(andUpdateSshKey, "rsa 私钥文件不存在或者有误");
if (repositoryModelReq.getRepoType() == RepositoryModel.RepoType.Git.getCode()) {
RepositoryModel repositoryModel = repositoryService.getByKey(repositoryModelReq.getId(), false);
if (repositoryModel != null) {
repositoryModelReq.setRsaPrv(StrUtil.emptyToDefault(repositoryModelReq.getRsaPrv(), repositoryModel.getRsaPrv()));
repositoryModelReq.setPassword(StrUtil.emptyToDefault(repositoryModelReq.getPassword(), repositoryModel.getPassword()));
}
// 验证 git 仓库信息
try {
Tuple tuple = GitUtil.getBranchAndTagList(repositoryModel);
Tuple tuple = GitUtil.getBranchAndTagList(repositoryModelReq);
} catch (JpomRuntimeException jpomRuntimeException) {
throw jpomRuntimeException;
} catch (Exception e) {
@ -134,6 +123,15 @@ public class RepositoryController extends BaseServerController {
return JsonMessage.toJson(500, "无法连接此仓库," + e.getMessage());
}
}
if (StrUtil.isEmpty(repositoryModelReq.getId())) {
// insert data
repositoryService.insert(repositoryModelReq);
} else {
// update data
repositoryModelReq.setWorkspaceId(repositoryService.getCheckUserWorkspace(getRequest()));
repositoryService.updateById(repositoryModelReq);
}
return JsonMessage.toJson(200, "操作成功");
}
@ -183,6 +181,8 @@ public class RepositoryController extends BaseServerController {
Validator.validateGeneral(repositoryModelReq.getId(), "错误的ID");
entity.set("id", "<> " + repositoryModelReq.getId());
}
String workspaceId = repositoryService.getCheckUserWorkspace(getRequest());
entity.set("workspaceId", workspaceId);
entity.set("gitUrl", repositoryModelReq.getGitUrl());
Assert.state(!repositoryService.exists(entity), "已经存在对应的仓库信息啦");
}
@ -196,20 +196,20 @@ public class RepositoryController extends BaseServerController {
if (repositoryModelReq.getProtocol() == GitProtocolEnum.SSH.getCode()) {
// if rsa key is not empty
if (StrUtil.isNotEmpty(repositoryModelReq.getRsaPrv())) {
File rsaFile = BuildUtil.getRepositoryRsaFile(repositoryModelReq.getId() + Const.ID_RSA);
/**
* if rsa key is start with "file:"
* copy this file
*/
if (StrUtil.startWith(repositoryModelReq.getRsaPrv(), URLUtil.FILE_URL_PREFIX)) {
String rsaPath = StrUtil.removePrefix(repositoryModelReq.getRsaPrv(), URLUtil.FILE_URL_PREFIX);
if (!FileUtil.file(rsaPath).exists()) {
if (!FileUtil.exist(rsaPath)) {
DefaultSystemLog.getLog().warn("there is no rsa file... {}", rsaPath);
return false;
}
} else {
//File rsaFile = BuildUtil.getRepositoryRsaFile(repositoryModelReq.getId() + Const.ID_RSA);
// or else put into file
FileUtil.writeUtf8String(repositoryModelReq.getRsaPrv(), rsaFile);
//FileUtil.writeUtf8String(repositoryModelReq.getRsaPrv(), rsaFile);
}
}
}
@ -232,6 +232,8 @@ public class RepositoryController extends BaseServerController {
Assert.state(!exists, "当前仓库被构建关联,不能直接删除");
repositoryService.delByKey(id, getRequest());
File rsaFile = BuildUtil.getRepositoryRsaFile(id + Const.ID_RSA);
FileUtil.del(rsaFile);
return JsonMessage.getString(200, "删除成功");
}
}

View File

@ -357,6 +357,8 @@ public class OutGivingProjectEditController extends BaseServerController {
allData.put("jvm", jvm);
String args = getParameter(StrUtil.format("{}_args", nodeModel.getId()));
allData.put("args", args);
String autoStart = getParameter(StrUtil.format("{}_autoStart", nodeModel.getId()));
allData.put("autoStart", Convert.toBool(autoStart, false));
// 项目副本
String javaCopyIds = getParameter(StrUtil.format("{}_javaCopyIds", nodeModel.getId()));
allData.put("javaCopyIds", javaCopyIds);

View File

@ -47,6 +47,7 @@ public class BuildInfoModel extends BaseWorkspaceModel {
/**
* 分组名称
*/
@Deprecated
private String group;
/**
* 分支
@ -85,6 +86,9 @@ public class BuildInfoModel extends BaseWorkspaceModel {
*/
private String extraData;
private String webhook;
public String getRepositoryId() {
return repositoryId;
}
@ -117,6 +121,14 @@ public class BuildInfoModel extends BaseWorkspaceModel {
this.group = group;
}
public String getWebhook() {
return webhook;
}
public void setWebhook(String webhook) {
this.webhook = webhook;
}
public String getBranchName() {
return branchName;
}

View File

@ -64,6 +64,7 @@ public class RepositoryModel extends BaseWorkspaceModel {
/**
* SSH RSA 公钥
*/
@Deprecated
private String rsaPub;
/**
* SSH RSA 私钥
@ -134,10 +135,12 @@ public class RepositoryModel extends BaseWorkspaceModel {
this.password = password;
}
@Deprecated
public String getRsaPub() {
return rsaPub;
}
@Deprecated
public void setRsaPub(String rsaPub) {
this.rsaPub = rsaPub;
}

View File

@ -101,6 +101,7 @@ public class InitDb implements DisposableBean, InitializingBean {
"classpath:/bin/h2-db-v1.sql",
"classpath:/bin/h2-db-v1.1.sql",
"classpath:/bin/h2-db-v2.sql",
"classpath:/bin/h2-db-v2.1.sql",
"classpath:/bin/h2-db-v3.sql",
};
// 加载 sql 变更记录避免重复执行

View File

@ -0,0 +1,4 @@
-- @author bwcx_jzy
ALTER TABLE BUILD_INFO
ADD IF NOT EXISTS webhook VARCHAR (255) comment 'webhook';

View File

@ -71,6 +71,7 @@ export function editBuild(params) {
repoType: params.repoType,
// 其他参数
extraData: params.extraData,
webhook: params.webhook,
};
return axios({
url: "/build/edit",

View File

@ -142,6 +142,7 @@ export function editProject(params, replicaParams) {
javaCopyIds: params.javaCopyIds,
token: params.token,
logPath: params.logPath,
autoStart: params.autoStart,
...replicaParams,
};
return axios({

View File

@ -181,6 +181,22 @@
<a-form-model-item v-if="temp.releaseMethod === 2 || temp.releaseMethod === 3" label="清空发布" prop="clearOld">
<a-switch v-model="tempExtraData.clearOld" checked-children="" un-checked-children="" />
</a-form-model-item>
<a-form-model-item prop="webhook">
<template slot="label">
WebHooks
<a-tooltip v-show="!temp.id">
<template slot="title">
<ul>
<li>构建过程请求对应的地址,开始构建,构建完成,开始发布,发布完成,构建异常,发布异常</li>
<li>传人参数有buildIdbuildNametypeerror</li>
<li>type 的值有startReadypullexecuteCommandreleasedonestopsuccess</li>
</ul>
</template>
<a-icon type="question-circle" theme="filled" />
</a-tooltip>
</template>
<a-input v-model="temp.webhook" placeholder="构建过程请求,非必填GET请求" />
</a-form-model-item>
</a-form-model>
</a-modal>
<!-- 触发器 -->
@ -253,7 +269,7 @@ export default {
{ title: "顺序重启(有重启失败将继续)", value: 3 },
],
columns: [
{ title: "名称", dataIndex: "name", width: 150, sorter: true, ellipsis: true, scopedSlots: { customRender: "name" } },
{ title: "名称", dataIndex: "name", width: 100, sorter: true, ellipsis: true, scopedSlots: { customRender: "name" } },
// { title: "", dataIndex: "group", key: "group%", sorter: true, width: 100, ellipsis: true, scopedSlots: { customRender: "group" } },
{
title: "分支",
@ -627,19 +643,27 @@ export default {
},
//
handleStartBuild(record) {
this.temp = Object.assign(record);
startBuild(this.temp.id).then((res) => {
if (res.code === 200) {
this.$notification.success({
message: res.msg,
this.$confirm({
title: "系统提示",
content: "确定要开始构建 【名称:" + record.name + "】 【分支:" + record.branchName + "】 吗?",
okText: "确认",
cancelText: "取消",
onOk: () => {
this.temp = Object.assign(record);
startBuild(this.temp.id).then((res) => {
if (res.code === 200) {
this.$notification.success({
message: res.msg,
});
this.handleFilter();
//
this.handleBuildLog({
id: this.temp.id,
buildId: res.data,
});
}
});
this.handleFilter();
//
this.handleBuildLog({
id: this.temp.id,
buildId: res.data,
});
}
},
});
},
//

View File

@ -230,15 +230,39 @@
</a-form-model-item>
<a-collapse v-show="temp.runMode && temp.runMode !== 'File'">
<a-collapse-panel v-for="nodeId in temp.nodeIdList" :key="nodeId" :header="nodeId">
<a-form-model-item label="WebHooks" prop="token">
<a-input v-model="temp[`${nodeId}_token`]" placeholder="关闭程序时自动请求,非必填GET请求" />
</a-form-model-item>
<a-form-model-item label="JVM 参数" prop="jvm">
<a-textarea v-model="temp[`${nodeId}_jvm`]" :auto-size="{ minRows: 3, maxRows: 3 }" placeholder="jvm参数,非必填.如:-Xms512m -Xmx512m" />
</a-form-model-item>
<a-form-model-item label="args 参数" prop="args">
<a-textarea v-model="temp[`${nodeId}_args`]" :auto-size="{ minRows: 3, maxRows: 3 }" placeholder="Main 函数 args 参数,非必填. 如:--server.port=8080" />
</a-form-model-item>
<a-form-model-item prop="autoStart">
<template slot="label">
自启动
<a-tooltip v-show="temp.type !== 'edit'">
<template slot="title">插件端启动的时候检查项目状态如果项目状态是未运行则尝试执行启动项目</template>
<a-icon type="question-circle" theme="filled" />
</a-tooltip>
</template>
<a-switch v-model="temp[`${nodeId}_autoStart`]" checked-children="" un-checked-children="" />
</a-form-model-item>
<a-form-model-item prop="token">
<template slot="label">
WebHooks
<a-tooltip v-show="temp.type !== 'edit'">
<template slot="title">
<ul>
<li>项目启动,停止,重启都将请求对应的地址</li>
<li>传人参数有projectIdprojectNametypecopyIdresult</li>
<li>type 的值有stopbeforeStopstartbeforeRestart</li>
</ul>
</template>
<a-icon type="question-circle" theme="filled" />
</a-tooltip>
</template>
<a-input v-model="temp[`${nodeId}_token`]" placeholder="项目启动,停止,重启都将请求对应的地址,非必填GET请求" />
</a-form-model-item>
<!-- 副本信息 -->
<a-row v-for="replica in temp[`${nodeId}_javaCopyItemList`]" :key="replica.id">
<a-form-model-item :label="`副本 ${replica.id} JVM 参数`" prop="jvm">
@ -641,6 +665,8 @@ export default {
this.temp[`${ele.nodeId}_jvm`] = res.data.jvm || "";
this.temp[`${ele.nodeId}_token`] = res.data.token || "";
this.temp[`${ele.nodeId}_args`] = res.data.args || "";
this.temp[`${ele.nodeId}_autoStart`] = res.data.autoStart;
// javaCopyItemList
this.temp[`${ele.nodeId}_javaCopyItemList`] = res.data.javaCopyItemList || [];
this.temp = { ...this.temp };

View File

@ -46,7 +46,14 @@
<a-select-option v-for="project in nodeProjectList" :key="project.id">{{ project.nodeName }}{{ project.name }}</a-select-option>
</a-select>
</a-form-model-item>
<a-form-model-item label="报警联系人" prop="notifyUser" class="jpom-notify">
<a-form-model-item prop="notifyUser" class="jpom-notify">
<template slot="label">
报警联系人
<a-tooltip v-show="!temp.id">
<template slot="title"> 如果这里的报警联系人无法选择说明这里面的管理员没有设置邮箱在右上角下拉菜单里面的用户资料里可以设置 </template>
<a-icon type="question-circle" theme="filled" />
</a-tooltip>
</template>
<a-transfer
:data-source="userList"
:lazy="false"

View File

@ -4,7 +4,6 @@
<a-input v-model="listQuery['%name%']" placeholder="监控名称" class="search-input-item" />
<a-button type="primary" @click="loadData">搜索</a-button>
<a-button type="primary" @click="handleAdd">新增</a-button>
</div>
<!-- 数据表格 -->
<a-table :data-source="list" :loading="loading" :columns="columns" :pagination="this.pagination" @change="changePage" bordered :rowKey="(record, index) => index">
@ -64,7 +63,14 @@
@change="handleMethodFeatureChange"
/>
</a-form-model-item>
<a-form-model-item label="报警联系人" prop="notifyUser" class="jpom-monitor-notify">
<a-form-model-item prop="notifyUser" class="jpom-monitor-notify">
<template slot="label">
报警联系人
<a-tooltip v-show="!temp.id">
<template slot="title"> 如果这里的报警联系人无法选择说明这里面的管理员没有设置邮箱在右上角下拉菜单里面的用户资料里可以设置 </template>
<a-icon type="question-circle" theme="filled" />
</a-tooltip>
</template>
<a-transfer :data-source="userList" :lazy="false" show-search :filter-option="filterOption" :target-keys="notifyUserKeys" :render="(item) => item.title" @change="handleNotifyUserChange" />
</a-form-model-item>
</a-form-model>
@ -267,28 +273,24 @@ export default {
if (this.monitorUserKeys.length === 0) {
this.$notification.error({
message: "请选择监控用户",
});
return false;
}
if (this.methodFeatureKeys.length === 0) {
this.$notification.error({
message: "请选择监控操作",
});
return false;
}
if (this.classFeatureKeys.length === 0) {
this.$notification.error({
message: "请选择监控的功能",
});
return false;
}
if (this.notifyUserKeys.length === 0) {
this.$notification.error({
message: "请选择报警联系人",
});
return false;
}
@ -303,7 +305,6 @@ export default {
//
this.$notification.success({
message: res.msg,
});
this.$refs["editMonitorForm"].resetFields();
this.editOperateMonitorVisible = false;
@ -325,7 +326,6 @@ export default {
if (res.code === 200) {
this.$notification.success({
message: res.msg,
});
this.loadData();
}

View File

@ -203,8 +203,31 @@
</template>
<a-button type="primary" @click="handleAddReplica">添加副本</a-button>
</a-form-model-item>
<a-form-model-item label="WebHooks" prop="token" v-show="temp.runMode && temp.runMode !== 'File'" class="jpom-node-project-token">
<a-input v-model="temp.token" placeholder="关闭程序时自动请求,非必填GET请求" />
<a-form-model-item prop="autoStart">
<template slot="label">
自启动
<a-tooltip v-show="temp.type !== 'edit'">
<template slot="title">插件端启动的时候检查项目状态如果项目状态是未运行则尝试执行启动项目</template>
<a-icon type="question-circle" theme="filled" />
</a-tooltip>
</template>
<a-switch v-model="temp.autoStart" checked-children="" un-checked-children="" />
</a-form-model-item>
<a-form-model-item prop="token" v-show="temp.runMode && temp.runMode !== 'File'" class="jpom-node-project-token">
<template slot="label">
WebHooks
<a-tooltip v-show="temp.type !== 'edit'">
<template slot="title">
<ul>
<li>项目启动,停止,重启都将请求对应的地址</li>
<li>传人参数有projectIdprojectNametypecopyIdresult</li>
<li>type 的值有stopbeforeStopstartbeforeRestart</li>
</ul>
</template>
<a-icon type="question-circle" theme="filled" />
</a-tooltip>
</template>
<a-input v-model="temp.token" placeholder="项目启动,停止,重启都将请求对应的地址,非必填GET请求" />
</a-form-model-item>
<a-form-model-item v-show="temp.type === 'edit' && temp.runMode !== 'File'" label="日志路径" prop="log">
<a-alert :message="temp.log" type="success" />
@ -505,7 +528,6 @@ export default {
if (this.temp.outGivingProject) {
this.$notification.warning({
message: "独立的项目分发请到分发管理中去修改",
});
return;
}
@ -531,7 +553,6 @@ export default {
if (res.code === 200) {
this.$notification.success({
message: res.msg,
});
this.$refs["editProjectForm"].resetFields();
this.editProjectVisible = false;
@ -600,7 +621,6 @@ export default {
if (res.code === 200) {
this.$notification.success({
message: res.msg,
});
this.loadData();
}
@ -625,8 +645,7 @@ export default {
if (res.code !== 200) {
this.$notification.warning({
message: res.msg,
description: '提示',
description: "提示",
});
}
});
@ -666,7 +685,6 @@ export default {
if (this.selectedRows.length == 0) {
this.$notification.warning({
message: "请选中要启动的项目",
});
}
this.selectedRows.forEach((value) => {
@ -687,7 +705,6 @@ export default {
if (this.selectedRows.length == 0) {
this.$notification.warning({
message: "请选中要重启的项目",
});
}
this.selectedRows.forEach((value) => {
@ -707,7 +724,6 @@ export default {
if (this.selectedRows.length == 0) {
this.$notification.warning({
message: "请选中要关闭的项目",
});
}
this.selectedRows.forEach((value) => {

View File

@ -253,7 +253,7 @@ export default {
[nodeId]: {
...data,
type: "uploading",
percent: (completeSize / size) * 100,
percent: Math.floor((completeSize / size) * 100),
},
});
},

View File

@ -4,7 +4,7 @@
<div ref="filter" class="filter">
<a-input class="search-input-item" v-model="listQuery['%name%']" placeholder="仓库名" />
<a-input class="search-input-item" v-model="listQuery['%gitUrl%']" placeholder="仓库地址" />
<a-select v-model="listQuery.repoType" allowClear placeholder="请选择仓库类型" class="filter-item" @change="handleFilter">
<a-select v-model="listQuery.repoType" allowClear placeholder="请选择仓库类型" class="filter-item">
<a-select-option :value="'0'">GIT</a-select-option>
<a-select-option :value="'1'">SVN</a-select-option>
</a-select>
@ -110,7 +110,7 @@
<a-textarea :auto-size="{ minRows: 3, maxRows: 3 }" v-model="temp.rsaPub" placeholder="公钥,不填将使用默认的 $HOME/.ssh 目录中的配置。支持配置文件目录:file:"></a-textarea>
</a-form-model-item>
</template>
<a-form-model-item v-if="temp.id">
<a-form-model-item v-if="temp.id" prop="restHideField">
<template slot="label">
隐藏字段
<a-tooltip>
@ -224,10 +224,10 @@ export default {
this.loading = false;
});
},
//
handleFilter() {
this.loadData();
},
// //
// handleFilter() {
// this.loadData();
// },
//
handleAdd() {
this.temp = {
@ -258,11 +258,10 @@ export default {
//
this.$notification.success({
message: res.msg,
});
this.$refs["editForm"].resetFields();
this.editVisible = false;
this.handleFilter();
this.loadData();
this.$refs["editForm"].resetFields();
}
});
});
@ -277,14 +276,13 @@ export default {
onOk: () => {
const params = {
id: record.id,
isRealDel: this.isSystem,
//isRealDel: this.isSystem,
};
//
deleteRepository(params).then((res) => {
if (res.code === 200) {
this.$notification.success({
message: res.msg,
});
this.loadData();
}
@ -306,7 +304,6 @@ export default {
if (res.code === 200) {
this.$notification.success({
message: res.msg,
});
this.loadData();
}

View File

@ -1,52 +1,73 @@
// vue.config.js
const IP = '127.0.0.1'
const IP = "127.0.0.1";
const Timestamp = new Date().getTime();
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
// 输出目录
outputDir: '../modules/server/src/main/resources/dist',
outputDir: "../modules/server/src/main/resources/dist",
// 控制静态资源使用相对路径
publicPath: './',
publicPath: "./",
// 代理设置
devServer: {
port: 3000,
proxy: {
// websocket
'/ssh': {
"/ssh": {
target: `wss://${IP}:2122`,
// true/false: if you want to proxy websockets
ws: false,
secure: false,
},
'/tomcat_log': {
"/tomcat_log": {
target: `wss://${IP}:2122`,
// true/false: if you want to proxy websockets
ws: false,
secure: false,
},
'/console': {
"/console": {
target: `wss://${IP}:2122`,
// true/false: if you want to proxy websockets
ws: false,
secure: false,
},
'/script_run': {
"/script_run": {
target: `wss://${IP}:2122`,
// true/false: if you want to proxy websockets
ws: false,
secure: false,
},
// http
'/*': {
"/*": {
target: `http://${IP}:2122`,
timeout: 10 * 60 * 1000
}
timeout: 10 * 60 * 1000,
},
},
},
chainWebpack: config => {
config.plugin('html')
.tap(args => {
args[0].title= 'Jpom项目管理系统'
args[0].build= new Date().getTime()
return args
})
}
}
configureWebpack: {
// name: name,
// 修改打包后的js文件名称
output: {
// 输出重构 打包编译后的 文件名称 【模块名称.版本号.时间戳】
filename: `js/[name].[hash].${Timestamp}.js`,
chunkFilename: `js/[name].[hash].${Timestamp}.js`,
},
// 修改打包后的css文件名称
plugins: [
new MiniCssExtractPlugin({
filename: `css/[name].[contenthash].${Timestamp}.css`,
}),
],
// resolve: {
// alias: {
// "@": resolve("src")
// }
// }
},
chainWebpack: (config) => {
config.plugin("html").tap((args) => {
args[0].title = "Jpom项目管理系统";
args[0].build = new Date().getTime();
return args;
});
},
};