mirror of
https://gitee.com/dromara/Jpom.git
synced 2024-11-30 02:48:17 +08:00
feat 构建项目发布支持配置发布到二级目录
This commit is contained in:
parent
3d37b26cac
commit
15aaf2b7c5
@ -5,6 +5,8 @@
|
||||
1. 【all】外置 `logback` 配置文件
|
||||
2. 【server】服务端管理相关功能独立页面菜单
|
||||
3. 【server】新增项目触发器用于管理项目状态
|
||||
4. 【all】新增 构建项目发布支持配置发布到二级目录
|
||||
5. 【server】新增 节点分发发布支持配置发布到二级目录
|
||||
|
||||
### 🐞 解决BUG、优化功能
|
||||
|
||||
|
@ -26,6 +26,7 @@ import cn.hutool.core.collection.CollStreamUtil;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.lang.Opt;
|
||||
import cn.hutool.core.thread.ThreadUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.BooleanUtil;
|
||||
@ -130,7 +131,7 @@ public class ProjectFileControl extends BaseAgentController {
|
||||
List<DiffFileVo.DiffItem> data = diffFileVo.getData();
|
||||
Assert.notEmpty(data, "没有要对比的数据");
|
||||
// 扫描项目目录下面的所有文件
|
||||
String path = projectInfoModel.allLib();
|
||||
String path = FileUtil.file(projectInfoModel.allLib(), Opt.ofBlankAble(diffFileVo.getDir()).orElse(StrUtil.SLASH)).getAbsolutePath();
|
||||
List<File> files = FileUtil.loopFiles(path);
|
||||
// 将所有的文件信息组装并签名
|
||||
List<JSONObject> collect = files.stream().map(file -> {
|
||||
@ -336,6 +337,7 @@ public class ProjectFileControl extends BaseAgentController {
|
||||
@RequestMapping(value = "batch_delete", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public JsonMessage<String> batchDelete(@RequestBody DiffFileVo diffFileVo) {
|
||||
String id = diffFileVo.getId();
|
||||
String dir = diffFileVo.getDir();
|
||||
NodeProjectInfoModel projectInfoModel = super.getProjectInfoModel(id);
|
||||
// 备份文件
|
||||
String backupId = ProjectFileBackupUtil.backup(projectInfoModel.getId(), projectInfoModel.allLib());
|
||||
@ -344,13 +346,13 @@ public class ProjectFileControl extends BaseAgentController {
|
||||
List<DiffFileVo.DiffItem> data = diffFileVo.getData();
|
||||
Assert.notEmpty(data, "没有要对比的数据");
|
||||
//
|
||||
String path = projectInfoModel.allLib();
|
||||
File path = FileUtil.file(projectInfoModel.allLib(), Opt.ofBlankAble(dir).orElse(StrUtil.SLASH));
|
||||
for (DiffFileVo.DiffItem datum : data) {
|
||||
File file = FileUtil.file(path, datum.getName());
|
||||
if (FileUtil.del(file)) {
|
||||
continue;
|
||||
}
|
||||
return new JsonMessage<>(500, "删除失败");
|
||||
return new JsonMessage<>(500, "删除失败:" + file.getAbsolutePath());
|
||||
}
|
||||
return JsonMessage.success("删除成功");
|
||||
} finally {
|
||||
|
@ -22,57 +22,39 @@
|
||||
*/
|
||||
package io.jpom.controller.manage.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author bwcx_jzy
|
||||
* @since 2021/12/16
|
||||
*/
|
||||
@Data
|
||||
public class DiffFileVo {
|
||||
|
||||
private String id;
|
||||
private List<DiffItem> data;
|
||||
/**
|
||||
* 项目id
|
||||
*/
|
||||
private String id;
|
||||
/**
|
||||
* 需要对比的数据
|
||||
*/
|
||||
private List<DiffItem> data;
|
||||
/**
|
||||
* 需要对比的目录
|
||||
*/
|
||||
private String dir;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public List<DiffItem> getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setData(List<DiffItem> data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public static class DiffItem {
|
||||
/**
|
||||
* 名称
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 文件签名
|
||||
*/
|
||||
private String sha1;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getSha1() {
|
||||
return sha1;
|
||||
}
|
||||
|
||||
public void setSha1(String sha1) {
|
||||
this.sha1 = sha1;
|
||||
}
|
||||
}
|
||||
@Data
|
||||
public static class DiffItem {
|
||||
/**
|
||||
* 名称
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 文件签名
|
||||
*/
|
||||
private String sha1;
|
||||
}
|
||||
}
|
||||
|
@ -39,6 +39,7 @@ import io.jpom.system.AgentConfig;
|
||||
import io.jpom.system.ConfigBean;
|
||||
import io.jpom.util.CommandUtil;
|
||||
import io.jpom.util.StringUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.io.File;
|
||||
@ -51,6 +52,7 @@ import java.util.stream.Collectors;
|
||||
* @author bwcx_jzy
|
||||
* @since 2022/5/10
|
||||
*/
|
||||
@Slf4j
|
||||
public class ProjectFileBackupUtil {
|
||||
|
||||
/**
|
||||
@ -152,47 +154,51 @@ public class ProjectFileBackupUtil {
|
||||
}
|
||||
// 考虑到大文件对比,比较耗时。需要异步对比文件
|
||||
ThreadUtil.execute(() -> {
|
||||
File backupItemPath = ProjectFileBackupUtil.path(pathId, backupId);
|
||||
File backupPath = ProjectFileBackupUtil.path(pathId);
|
||||
// 获取文件列表
|
||||
Map<String, File> backupFiles = ProjectFileBackupUtil.listFiles(backupItemPath.getAbsolutePath());
|
||||
Map<String, File> nowFiles = ProjectFileBackupUtil.listFiles(projectPath);
|
||||
nowFiles.forEach((fileSha1, file) -> {
|
||||
// 当前目录存在的,但是备份目录也存在的相同文件则删除
|
||||
File backupFile = backupFiles.get(fileSha1);
|
||||
if (backupFile != null) {
|
||||
CommandUtil.systemFastDel(backupFile);
|
||||
backupFiles.remove(fileSha1);
|
||||
}
|
||||
});
|
||||
// 判断保存指定后缀
|
||||
String[] backupSuffix = Optional.ofNullable(dslYmlDto)
|
||||
.map(DslYmlDto::getFile)
|
||||
.map(DslYmlDto.FileConfig::getBackupSuffix)
|
||||
.orElseGet(() -> {
|
||||
AgentConfig agentConfig = SpringUtil.getBean(AgentConfig.class);
|
||||
AgentConfig.ProjectConfig project = agentConfig.getProject();
|
||||
return project.getFileBackupSuffix();
|
||||
try {
|
||||
File backupItemPath = ProjectFileBackupUtil.path(pathId, backupId);
|
||||
File backupPath = ProjectFileBackupUtil.path(pathId);
|
||||
// 获取文件列表
|
||||
Map<String, File> backupFiles = ProjectFileBackupUtil.listFiles(backupItemPath.getAbsolutePath());
|
||||
Map<String, File> nowFiles = ProjectFileBackupUtil.listFiles(projectPath);
|
||||
nowFiles.forEach((fileSha1, file) -> {
|
||||
// 当前目录存在的,但是备份目录也存在的相同文件则删除
|
||||
File backupFile = backupFiles.get(fileSha1);
|
||||
if (backupFile != null) {
|
||||
CommandUtil.systemFastDel(backupFile);
|
||||
backupFiles.remove(fileSha1);
|
||||
}
|
||||
});
|
||||
if (ArrayUtil.isNotEmpty(backupSuffix)) {
|
||||
backupFiles.values()
|
||||
.stream()
|
||||
.filter(file -> {
|
||||
String name = FileUtil.getName(file);
|
||||
for (String reg : backupSuffix) {
|
||||
if (ReUtil.isMatch(reg, name)) {
|
||||
// 满足正则条件
|
||||
return false;
|
||||
// 判断保存指定后缀
|
||||
String[] backupSuffix = Optional.ofNullable(dslYmlDto)
|
||||
.map(DslYmlDto::getFile)
|
||||
.map(DslYmlDto.FileConfig::getBackupSuffix)
|
||||
.orElseGet(() -> {
|
||||
AgentConfig agentConfig = SpringUtil.getBean(AgentConfig.class);
|
||||
AgentConfig.ProjectConfig project = agentConfig.getProject();
|
||||
return project.getFileBackupSuffix();
|
||||
});
|
||||
if (ArrayUtil.isNotEmpty(backupSuffix)) {
|
||||
backupFiles.values()
|
||||
.stream()
|
||||
.filter(file -> {
|
||||
String name = FileUtil.getName(file);
|
||||
for (String reg : backupSuffix) {
|
||||
if (ReUtil.isMatch(reg, name)) {
|
||||
// 满足正则条件
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return !StrUtil.endWithAny(name, backupSuffix);
|
||||
})
|
||||
.forEach(CommandUtil::systemFastDel);
|
||||
return !StrUtil.endWithAny(name, backupSuffix);
|
||||
})
|
||||
.forEach(CommandUtil::systemFastDel);
|
||||
}
|
||||
// 删除空文件夹
|
||||
loopClean(backupItemPath);
|
||||
// 检查备份保留个数
|
||||
clearOldBackup(backupPath, dslYmlDto);
|
||||
} catch (Exception e) {
|
||||
log.warn("对比清空项目文件备份失败", e);
|
||||
}
|
||||
// 删除空文件夹
|
||||
loopClean(backupItemPath);
|
||||
// 检查备份保留个数
|
||||
clearOldBackup(backupPath, dslYmlDto);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -61,6 +61,14 @@ public class AgentAuthorize implements InitializingBean {
|
||||
*/
|
||||
private String authorize;
|
||||
|
||||
public void setAuthorize(String authorize) {
|
||||
// 不能外部 set
|
||||
}
|
||||
|
||||
public String getAuthorize() {
|
||||
throw new UnsupportedOperationException("不能调用此方法");
|
||||
}
|
||||
|
||||
private final ConfigBean configBean;
|
||||
/**
|
||||
* 注入控制加载顺序,必须先加载数据目录才能初始化
|
||||
@ -123,9 +131,13 @@ public class AgentAuthorize implements InitializingBean {
|
||||
if (StrUtil.isEmpty(this.agentName)) {
|
||||
throw new JpomRuntimeException("The agent login name cannot be empty");
|
||||
}
|
||||
this.checkPwd();
|
||||
// 生成密码授权字符串
|
||||
this.authorize = SecureUtil.sha1(this.agentName + "@" + this.agentPwd);
|
||||
if (StrUtil.isEmpty(this.authorize)) {
|
||||
this.checkPwd();
|
||||
// 生成密码授权字符串
|
||||
this.authorize = SecureUtil.sha1(this.agentName + "@" + this.agentPwd);
|
||||
} else {
|
||||
log.warn("authorized 不能重复加载");
|
||||
}
|
||||
//
|
||||
JvmUtil.checkJpsNormal();
|
||||
}
|
||||
|
@ -13,9 +13,9 @@ jpom:
|
||||
# 停止、启动项目(项目状态检测)等待的时长 单位秒
|
||||
status-wait-time: 10
|
||||
# 项目文件备份保留个数,大于 0 才会备份
|
||||
file-backup-count: 0
|
||||
file-backup-count: 1
|
||||
# 限制备份指定文件后缀(支持正则)
|
||||
#file-backup-suffix: [ '.jar','.html','^.+\\.(?i)(txt)$' ]
|
||||
file-backup-suffix: [ '.jar','.html','^.+\\.(?i)(txt)$' ]
|
||||
# 项目状态检测间隔时间 单位毫秒,最小为1毫秒
|
||||
status-detection-interval: 500
|
||||
log:
|
||||
|
@ -64,13 +64,15 @@ public class WebAopLog {
|
||||
Object proceed;
|
||||
Object logResult = null;
|
||||
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
|
||||
String requestUri = requestAttributes.getRequest().getRequestURI();
|
||||
try {
|
||||
aopLogInterface.forEach(aopLogInterface -> aopLogInterface.before(joinPoint));
|
||||
proceed = joinPoint.proceed();
|
||||
logResult = proceed;
|
||||
log.debug("{} {}", requestAttributes.getRequest().getRequestURI(), Optional.ofNullable(proceed).orElse(StrUtil.EMPTY));
|
||||
log.debug("{} {}", requestUri, Optional.ofNullable(proceed).orElse(StrUtil.EMPTY));
|
||||
} catch (Throwable e) {
|
||||
log.debug("{}", requestAttributes.getRequest().getRequestURI(), e);
|
||||
// 不用记录异常日志,全局异常拦截里面会记录,此处不用重复记录
|
||||
// log.debug("发生异常 {}", requestUri, e);
|
||||
logResult = e;
|
||||
throw e;
|
||||
} finally {
|
||||
|
@ -32,6 +32,7 @@ import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.Lombok;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
|
||||
import java.io.File;
|
||||
@ -41,6 +42,7 @@ import java.nio.file.Path;
|
||||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@ -260,4 +262,19 @@ public class FileUtils {
|
||||
});
|
||||
return paths;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断目录是否有越级问题
|
||||
*
|
||||
* @param dir 目录
|
||||
* @param function 异常
|
||||
*/
|
||||
public static void checkSlip(String dir, Function<Exception, Exception> function) {
|
||||
try {
|
||||
File userHomeDir = FileUtil.getUserHomeDir();
|
||||
FileUtil.checkSlip(userHomeDir, FileUtil.file(userHomeDir, dir));
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw Lombok.sneakyThrow(function.apply(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -151,6 +151,10 @@ public class BuildExtraModule extends BaseModel {
|
||||
* 镜像标签
|
||||
*/
|
||||
private String dockerImagesLabels;
|
||||
/**
|
||||
* 项目二级目录
|
||||
*/
|
||||
private String projectSecondaryDirectory;
|
||||
|
||||
public String getResultDirFile() {
|
||||
if (resultDirFile == null) {
|
||||
|
@ -30,6 +30,7 @@ import cn.hutool.core.date.SystemClock;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.io.resource.ResourceUtil;
|
||||
import cn.hutool.core.lang.Opt;
|
||||
import cn.hutool.core.text.CharPool;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
@ -494,6 +495,9 @@ public class ReleaseManage implements Runnable {
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.put("id", projectId);
|
||||
jsonObject.put("data", collect);
|
||||
String directory = this.buildExtraModule.getProjectSecondaryDirectory();
|
||||
directory = Opt.ofBlankAble(directory).orElse(StrUtil.SLASH);
|
||||
jsonObject.put("dir", directory);
|
||||
JsonMessage<JSONObject> requestBody = NodeForward.requestBody(nodeModel, NodeUrl.MANAGE_FILE_DIFF_FILE, this.userModel, jsonObject);
|
||||
if (requestBody.getCode() != HttpStatus.HTTP_OK) {
|
||||
throw new JpomRuntimeException("对比项目文件失败:" + requestBody);
|
||||
@ -523,6 +527,7 @@ public class ReleaseManage implements Runnable {
|
||||
File file = FileUtil.file(resultFileParent, name);
|
||||
//
|
||||
String startPath = StringUtil.delStartPath(file, resultFileParent, false);
|
||||
startPath = FileUtil.normalize(startPath + StrUtil.SLASH + directory);
|
||||
//
|
||||
JsonMessage<String> jsonMessage = OutGivingRun.fileUpload(file, startPath,
|
||||
projectId, false, last ? afterOpt : AfterOpt.No, nodeModel, this.userModel, false);
|
||||
@ -540,7 +545,7 @@ public class ReleaseManage implements Runnable {
|
||||
* 发布项目
|
||||
*/
|
||||
private void doProject() {
|
||||
// AfterOpt afterOpt, boolean clearOld, boolean diffSync
|
||||
//AfterOpt afterOpt, boolean clearOld, boolean diffSync
|
||||
AfterOpt afterOpt = BaseEnum.getEnum(AfterOpt.class, this.buildExtraModule.getAfterOpt(), AfterOpt.No);
|
||||
boolean clearOld = this.buildExtraModule.isClearOld();
|
||||
boolean diffSync = this.buildExtraModule.isDiffSync();
|
||||
@ -563,7 +568,7 @@ public class ReleaseManage implements Runnable {
|
||||
zipFile = this.resultFile;
|
||||
unZip = false;
|
||||
}
|
||||
JsonMessage<String> jsonMessage = OutGivingRun.fileUpload(zipFile, null,
|
||||
JsonMessage<String> jsonMessage = OutGivingRun.fileUpload(zipFile, this.buildExtraModule.getProjectSecondaryDirectory(),
|
||||
projectId,
|
||||
unZip,
|
||||
afterOpt,
|
||||
|
@ -24,6 +24,7 @@ package io.jpom.controller.build;
|
||||
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.lang.Opt;
|
||||
import cn.hutool.core.lang.RegexPool;
|
||||
import cn.hutool.core.lang.Tuple;
|
||||
import cn.hutool.core.lang.Validator;
|
||||
@ -60,6 +61,7 @@ import io.jpom.service.node.ssh.SshService;
|
||||
import io.jpom.service.script.ScriptServer;
|
||||
import io.jpom.system.extconf.BuildExtConfig;
|
||||
import io.jpom.util.CommandUtil;
|
||||
import io.jpom.util.FileUtils;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
@ -210,12 +212,7 @@ public class BuildInfoController extends BaseServerController {
|
||||
if (StrUtil.isNotEmpty(webhook)) {
|
||||
Validator.validateMatchRegex(RegexPool.URL_HTTP, webhook, "WebHooks 地址不合法");
|
||||
}
|
||||
try {
|
||||
File userHomeDir = FileUtil.getUserHomeDir();
|
||||
FileUtil.checkSlip(userHomeDir, FileUtil.file(userHomeDir, resultDirFile));
|
||||
} catch (Exception e) {
|
||||
return new JsonMessage<>(405, "产物目录不能越级:" + e.getMessage());
|
||||
}
|
||||
FileUtils.checkSlip(resultDirFile, e -> new IllegalArgumentException("产物目录不能越级:" + e.getMessage()));
|
||||
buildInfoModel.setAutoBuildCron(this.checkCron(autoBuildCron));
|
||||
buildInfoModel.setWebhook(webhook);
|
||||
buildInfoModel.setRepositoryId(repositoryId);
|
||||
@ -230,7 +227,7 @@ public class BuildInfoController extends BaseServerController {
|
||||
BuildReleaseMethod releaseMethod1 = BaseEnum.getEnum(BuildReleaseMethod.class, releaseMethod);
|
||||
Assert.notNull(releaseMethod1, "发布方法不正确");
|
||||
buildInfoModel.setReleaseMethod(releaseMethod1.getCode());
|
||||
// 把 extraData 信息转换成 JSON 字符串
|
||||
// 把 extraData 信息转换成 JSON 字符串 ,不能直接使用 io.jpom.build.BuildExtraModule
|
||||
JSONObject jsonObject = JSON.parseObject(extraData);
|
||||
|
||||
// 验证发布方式 和 extraData 信息
|
||||
@ -389,6 +386,11 @@ public class BuildInfoController extends BaseServerController {
|
||||
String clearOld = jsonObject.getString("clearOld");
|
||||
jsonObject.put("afterOpt", afterOpt1.getCode());
|
||||
jsonObject.put("clearOld", Convert.toBool(clearOld, false));
|
||||
//
|
||||
String projectSecondaryDirectory = jsonObject.getString("projectSecondaryDirectory");
|
||||
Opt.ofBlankAble(projectSecondaryDirectory).ifPresent(s -> {
|
||||
FileUtils.checkSlip(s, e -> new IllegalArgumentException("二级目录不能越级:" + e.getMessage()));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -93,7 +93,8 @@ public class BuildInfoManageController extends BaseServerController {
|
||||
String resultDirFile,
|
||||
String branchName,
|
||||
String branchTagName,
|
||||
String checkRepositoryDiff) {
|
||||
String checkRepositoryDiff,
|
||||
String projectSecondaryDirectory) {
|
||||
BuildInfoModel item = buildInfoService.getByKey(id, getRequest());
|
||||
Assert.notNull(item, "没有对应数据");
|
||||
// 更新数据
|
||||
@ -101,8 +102,16 @@ public class BuildInfoManageController extends BaseServerController {
|
||||
Opt.ofBlankAble(resultDirFile).ifPresent(update::setResultDirFile);
|
||||
Opt.ofBlankAble(branchName).ifPresent(update::setBranchName);
|
||||
Opt.ofBlankAble(branchTagName).ifPresent(update::setBranchTagName);
|
||||
Opt.ofBlankAble(projectSecondaryDirectory).ifPresent(s -> {
|
||||
FileUtils.checkSlip(s, e -> new IllegalArgumentException("二级目录不能越级:" + e.getMessage()));
|
||||
//
|
||||
String extraData = item.getExtraData();
|
||||
JSONObject jsonObject = JSONObject.parseObject(extraData);
|
||||
jsonObject.put("projectSecondaryDirectory", s);
|
||||
update.setExtraData(jsonObject.toString());
|
||||
});
|
||||
|
||||
if (!StrUtil.isAllBlank(resultDirFile, branchName, branchTagName)) {
|
||||
if (!StrUtil.isAllBlank(resultDirFile, branchName, branchTagName, projectSecondaryDirectory)) {
|
||||
update.setId(id);
|
||||
buildInfoService.update(update);
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ package io.jpom.controller.outgiving;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.lang.Opt;
|
||||
import cn.hutool.core.lang.Validator;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.db.Entity;
|
||||
@ -50,6 +51,7 @@ import io.jpom.service.dblog.BuildInfoService;
|
||||
import io.jpom.service.node.ProjectInfoCacheService;
|
||||
import io.jpom.service.outgiving.DbOutGivingLogService;
|
||||
import io.jpom.service.outgiving.OutGivingServer;
|
||||
import io.jpom.util.FileUtils;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
@ -207,7 +209,12 @@ public class OutGivingController extends BaseServerController {
|
||||
outGivingModel.setIntervalTime(intervalTime);
|
||||
//
|
||||
outGivingModel.setClearOld(Convert.toBool(getParameter("clearOld"), false));
|
||||
|
||||
//
|
||||
String secondaryDirectory = getParameter("secondaryDirectory");
|
||||
Opt.ofBlankAble(secondaryDirectory).ifPresent(s -> {
|
||||
FileUtils.checkSlip(s, e -> new IllegalArgumentException("二级目录不能越级:" + e.getMessage()));
|
||||
outGivingModel.setSecondaryDirectory(secondaryDirectory);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -181,7 +181,7 @@ public class OutGivingProjectController extends BaseServerController {
|
||||
*/
|
||||
@RequestMapping(value = "upload", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
@Feature(method = MethodFeature.UPLOAD)
|
||||
public JsonMessage<Object> upload(String id, String afterOpt, String clearOld, String autoUnzip) throws IOException {
|
||||
public JsonMessage<Object> upload(String id, String afterOpt, String clearOld, String autoUnzip, String secondaryDirectory) throws IOException {
|
||||
OutGivingModel outGivingModel = this.check(id);
|
||||
AfterOpt afterOpt1 = BaseEnum.getEnum(AfterOpt.class, Convert.toInt(afterOpt, 0));
|
||||
Assert.notNull(afterOpt1, "请选择分发后的操作");
|
||||
@ -201,6 +201,7 @@ public class OutGivingProjectController extends BaseServerController {
|
||||
//outGivingModel = outGivingServer.getItem(id);
|
||||
outGivingModel.setClearOld(Convert.toBool(clearOld, false));
|
||||
outGivingModel.setAfterOpt(afterOpt1.getCode());
|
||||
outGivingModel.setSecondaryDirectory(secondaryDirectory);
|
||||
|
||||
outGivingServer.update(outGivingModel);
|
||||
// 开启
|
||||
@ -228,7 +229,7 @@ public class OutGivingProjectController extends BaseServerController {
|
||||
*/
|
||||
@RequestMapping(value = "remote_download", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
@Feature(method = MethodFeature.REMOTE_DOWNLOAD)
|
||||
public JsonMessage<String> remoteDownload(String id, String afterOpt, String clearOld, String url, String autoUnzip) {
|
||||
public JsonMessage<String> remoteDownload(String id, String afterOpt, String clearOld, String url, String autoUnzip, String secondaryDirectory) {
|
||||
OutGivingModel outGivingModel = this.check(id);
|
||||
AfterOpt afterOpt1 = BaseEnum.getEnum(AfterOpt.class, Convert.toInt(afterOpt, 0));
|
||||
Assert.notNull(afterOpt1, "请选择分发后的操作");
|
||||
@ -242,6 +243,7 @@ public class OutGivingProjectController extends BaseServerController {
|
||||
//outGivingModel = outGivingServer.getItem(id);
|
||||
outGivingModel.setClearOld(Convert.toBool(clearOld, false));
|
||||
outGivingModel.setAfterOpt(afterOpt1.getCode());
|
||||
outGivingModel.setSecondaryDirectory(secondaryDirectory);
|
||||
outGivingServer.update(outGivingModel);
|
||||
//下载
|
||||
File file = FileUtil.file(serverConfig.getUserTempPath(), ServerConst.OUTGIVING_FILE, id);
|
||||
|
@ -24,6 +24,7 @@ package io.jpom.controller.outgiving;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.lang.Opt;
|
||||
import cn.hutool.core.lang.Tuple;
|
||||
import cn.hutool.core.lang.Validator;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
@ -51,6 +52,7 @@ import io.jpom.permission.MethodFeature;
|
||||
import io.jpom.service.dblog.BuildInfoService;
|
||||
import io.jpom.service.node.ProjectInfoCacheService;
|
||||
import io.jpom.service.outgiving.OutGivingServer;
|
||||
import io.jpom.util.FileUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.util.Assert;
|
||||
@ -404,7 +406,12 @@ public class OutGivingProjectEditController extends BaseServerController {
|
||||
deleteProject(outGivingModel, outGivingNodeProjects, userModel);
|
||||
|
||||
outGivingModel.outGivingNodeProjectList(outGivingNodeProjects);
|
||||
|
||||
//
|
||||
String secondaryDirectory = getParameter("secondaryDirectory");
|
||||
Opt.ofBlankAble(secondaryDirectory).ifPresent(s -> {
|
||||
FileUtils.checkSlip(s, e -> new IllegalArgumentException("二级目录不能越级:" + e.getMessage()));
|
||||
outGivingModel.setSecondaryDirectory(secondaryDirectory);
|
||||
});
|
||||
return tuples;
|
||||
}
|
||||
|
||||
|
@ -75,6 +75,11 @@ public class OutGivingModel extends BaseWorkspaceModel {
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 二级目录
|
||||
*/
|
||||
private String secondaryDirectory;
|
||||
|
||||
|
||||
public boolean clearOld() {
|
||||
return clearOld != null && clearOld;
|
||||
|
@ -69,7 +69,8 @@ public class OutGivingItemRun implements Callable<OutGivingNodeProject.Status> {
|
||||
private final UserModel userModel;
|
||||
private final boolean unzip;
|
||||
private final boolean clearOld;
|
||||
private Integer sleepTime;
|
||||
private final Integer sleepTime;
|
||||
private final String secondaryDirectory;
|
||||
/**
|
||||
* 数据库记录id
|
||||
*/
|
||||
@ -82,8 +83,9 @@ public class OutGivingItemRun implements Callable<OutGivingNodeProject.Status> {
|
||||
boolean unzip,
|
||||
Integer sleepTime) {
|
||||
this.outGivingId = item.getId();
|
||||
this.unzip = unzip;
|
||||
this.secondaryDirectory = item.getSecondaryDirectory();
|
||||
this.clearOld = item.clearOld();
|
||||
this.unzip = unzip;
|
||||
this.outGivingNodeProject = outGivingNodeProject;
|
||||
this.file = file;
|
||||
this.afterOpt = ObjectUtil.defaultIfNull(EnumUtil.likeValueOf(AfterOpt.class, item.getAfterOpt()), AfterOpt.No);
|
||||
@ -105,7 +107,7 @@ public class OutGivingItemRun implements Callable<OutGivingNodeProject.Status> {
|
||||
this.updateStatus(this.outGivingId, this.outGivingNodeProject,
|
||||
OutGivingNodeProject.Status.Ing, "开始分发");
|
||||
//
|
||||
JsonMessage<String> jsonMessage = OutGivingRun.fileUpload(file, null,
|
||||
JsonMessage<String> jsonMessage = OutGivingRun.fileUpload(file, this.secondaryDirectory,
|
||||
this.outGivingNodeProject.getProjectId(),
|
||||
unzip,
|
||||
afterOpt,
|
||||
|
@ -61,7 +61,7 @@ public class DbExtConfig {
|
||||
/**
|
||||
* 缓存大小
|
||||
* <p>
|
||||
* http://www.h2database.com/html/features.html#cache_settings
|
||||
* <a href="http://www.h2database.com/html/features.html#cache_settings">http://www.h2database.com/html/features.html#cache_settings</a>
|
||||
*/
|
||||
private DataSize cacheSize = DataSize.ofMegabytes(10);
|
||||
|
||||
|
@ -73,7 +73,7 @@ public class OperateLogController implements AopLogInterface {
|
||||
|
||||
private final DbUserOperateLogService dbUserOperateLogService;
|
||||
|
||||
private String[] logFilterPar = new String[]{"pwd", "pass", "password"};
|
||||
private final String[] logFilterPar = new String[]{"pwd", "pass", "password"};
|
||||
|
||||
|
||||
public OperateLogController(DbUserOperateLogService dbUserOperateLogService) {
|
||||
|
@ -25,3 +25,6 @@ ALTER TABLE NODE_INFO DROP COLUMN IF EXISTS `cycle`;
|
||||
|
||||
ALTER TABLE PROJECT_INFO
|
||||
ADD IF NOT EXISTS triggerToken VARCHAR (100) comment '触发器token';
|
||||
|
||||
ALTER TABLE OUT_GIVING
|
||||
ADD IF NOT EXISTS secondaryDirectory VARCHAR (200) comment '二级目录';
|
||||
|
@ -361,14 +361,19 @@
|
||||
</a-select>
|
||||
</a-form-model-item>
|
||||
<!-- 项目 -->
|
||||
<a-form-model-item v-if="temp.releaseMethod === 2" label="发布项目" prop="releaseMethodDataIdList">
|
||||
<a-cascader v-model="temp.releaseMethodDataIdList" :options="cascaderList" placeholder="请选择节点项目" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item v-if="temp.releaseMethod === 2" label="发布后操作" prop="afterOpt">
|
||||
<a-select show-search allowClear v-model="tempExtraData.afterOpt" placeholder="请选择发布后操作">
|
||||
<a-select-option v-for="opt in afterOptListSimple" :key="opt.value">{{ opt.title }}</a-select-option>
|
||||
</a-select>
|
||||
</a-form-model-item>
|
||||
<template v-if="temp.releaseMethod === 2">
|
||||
<a-form-model-item label="发布项目" prop="releaseMethodDataIdList">
|
||||
<a-cascader v-model="temp.releaseMethodDataIdList" :options="cascaderList" placeholder="请选择节点项目" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item label="发布后操作" prop="afterOpt">
|
||||
<a-select show-search allowClear v-model="tempExtraData.afterOpt" placeholder="请选择发布后操作">
|
||||
<a-select-option v-for="opt in afterOptListSimple" :key="opt.value">{{ opt.title }}</a-select-option>
|
||||
</a-select>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item prop="projectSecondaryDirectory" label="二级目录">
|
||||
<a-input v-model="tempExtraData.projectSecondaryDirectory" placeholder="不填写则发布至项目的根目录" />
|
||||
</a-form-model-item>
|
||||
</template>
|
||||
<!-- SSH -->
|
||||
<template v-if="temp.releaseMethod === 3">
|
||||
<a-form-model-item prop="releaseMethodDataId">
|
||||
@ -872,6 +877,10 @@
|
||||
</a-space>
|
||||
</a-form-model-item>
|
||||
|
||||
<a-form-model-item v-if="temp.releaseMethod === 2" prop="projectSecondaryDirectory" label="二级目录">
|
||||
<a-input v-model="temp.projectSecondaryDirectory" placeholder="不填写则发布至项目的根目录" />
|
||||
</a-form-model-item>
|
||||
|
||||
<a-form-model-item label="构建备注" prop="buildRemark">
|
||||
<a-textarea v-model="temp.buildRemark" :maxLength="240" placeholder="请输入构建备注,长度小于 240" :auto-size="{ minRows: 3, maxRows: 5 }" />
|
||||
</a-form-model-item>
|
||||
@ -1555,8 +1564,10 @@ export default {
|
||||
this.buildConfirmVisible = true;
|
||||
this.branchList = [];
|
||||
this.branchTagList = [];
|
||||
//
|
||||
try {
|
||||
this.temp = { ...this.temp, checkRepositoryDiff: (JSON.parse(record.extraData) || {}).checkRepositoryDiff };
|
||||
const extraData = JSON.parse(record.extraData) || {};
|
||||
this.temp = { ...this.temp, checkRepositoryDiff: extraData.checkRepositoryDiff, projectSecondaryDirectory: extraData.projectSecondaryDirectory };
|
||||
} catch (e) {
|
||||
//
|
||||
}
|
||||
@ -1570,6 +1581,7 @@ export default {
|
||||
branchTagName: this.temp.branchTagName,
|
||||
branchName: this.temp.branchName,
|
||||
checkRepositoryDiff: this.temp.checkRepositoryDiff,
|
||||
projectSecondaryDirectory: this.temp.projectSecondaryDirectory,
|
||||
},
|
||||
true
|
||||
).then(() => {
|
||||
|
@ -235,6 +235,9 @@
|
||||
</template>
|
||||
<a-input-number :min="0" v-model="temp.intervalTime" placeholder="分发间隔时间 (顺序重启、完整顺序重启)方式才生效" style="width: 100%" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item prop="secondaryDirectory" label="二级目录">
|
||||
<a-input v-model="temp.secondaryDirectory" placeholder="不填写则发布至项目的根目录" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item prop="clearOld">
|
||||
<template slot="label">
|
||||
清空发布
|
||||
@ -392,6 +395,9 @@
|
||||
</template>
|
||||
<a-input-number :min="0" v-model="temp.intervalTime" placeholder="分发间隔时间 (顺序重启、完整顺序重启)方式才生效" style="width: 100%" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item prop="secondaryDirectory" label="二级目录">
|
||||
<a-input v-model="temp.secondaryDirectory" placeholder="不填写则发布至项目的根目录" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item prop="clearOld">
|
||||
<template slot="label">
|
||||
清空发布
|
||||
@ -540,6 +546,9 @@
|
||||
<a-select-option v-for="item in afterOptList" :key="item.value">{{ item.title }}</a-select-option>
|
||||
</a-select>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item prop="secondaryDirectory" label="二级目录">
|
||||
<a-input v-model="temp.secondaryDirectory" placeholder="不填写则发布至项目的根目录" />
|
||||
</a-form-model-item>
|
||||
</a-form-model>
|
||||
</a-modal>
|
||||
<!-- 项目文件组件 -->
|
||||
@ -800,6 +809,7 @@ export default {
|
||||
id: record.id,
|
||||
intervalTime: record.intervalTime,
|
||||
clearOld: record.clearOld,
|
||||
secondaryDirectory: record.secondaryDirectory,
|
||||
};
|
||||
// console.log(this.temp);
|
||||
this.linkDispatchVisible = true;
|
||||
@ -927,6 +937,7 @@ export default {
|
||||
nodeIdList: [],
|
||||
intervalTime: record.intervalTime,
|
||||
clearOld: record.clearOld,
|
||||
secondaryDirectory: record.secondaryDirectory,
|
||||
};
|
||||
}
|
||||
// 添加 nodeIdList
|
||||
@ -1094,6 +1105,7 @@ export default {
|
||||
formData.append("afterOpt", this.temp.afterOpt);
|
||||
formData.append("clearOld", this.temp.clearOld);
|
||||
formData.append("autoUnzip", this.temp.autoUnzip);
|
||||
formData.append("secondaryDirectory", this.temp.secondaryDirectory);
|
||||
uploadDispatchFile(formData).then((res) => {
|
||||
if (res.code === 200) {
|
||||
this.$notification.success({
|
||||
|
@ -120,7 +120,7 @@
|
||||
</a-form-model-item>
|
||||
|
||||
<a-form-model-item label="SSH节点" required>
|
||||
<a-select show-search option-filter-prop="children" mode="multiple" v-model="chooseSsh">
|
||||
<a-select show-search option-filter-prop="children" mode="multiple" v-model="chooseSsh" placeholder="请选择 SSH节点">
|
||||
<a-select-option v-for="item in sshList" :key="item.id" :value="item.id">
|
||||
{{ item.name }}
|
||||
</a-select-option>
|
||||
|
Loading…
Reference in New Issue
Block a user