org.dromara.jpom.controller.node.manage.ProjectManageControl Maven / Gradle / Ivy
/*
* Copyright (c) 2019 Of Him Code Technology Studio
* Jpom is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
package org.dromara.jpom.controller.node.manage;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.io.CharsetDetector;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.Tuple;
import cn.hutool.core.text.StrPool;
import cn.hutool.core.text.csv.*;
import cn.hutool.core.util.EnumUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.db.Entity;
import cn.hutool.extra.servlet.ServletUtil;
import cn.keepbx.jpom.IJsonMessage;
import cn.keepbx.jpom.model.JsonMessage;
import com.alibaba.fastjson2.JSONObject;
import lombok.Lombok;
import lombok.extern.slf4j.Slf4j;
import org.dromara.jpom.common.BaseServerController;
import org.dromara.jpom.common.forward.NodeForward;
import org.dromara.jpom.common.forward.NodeUrl;
import org.dromara.jpom.common.i18n.I18nMessageUtil;
import org.dromara.jpom.common.validator.ValidatorItem;
import org.dromara.jpom.common.validator.ValidatorRule;
import org.dromara.jpom.model.BaseNodeModel;
import org.dromara.jpom.model.PageResultDto;
import org.dromara.jpom.model.RunMode;
import org.dromara.jpom.model.data.BuildInfoModel;
import org.dromara.jpom.model.data.MonitorModel;
import org.dromara.jpom.model.data.NodeModel;
import org.dromara.jpom.model.data.RepositoryModel;
import org.dromara.jpom.model.enums.BuildReleaseMethod;
import org.dromara.jpom.model.node.ProjectInfoCacheModel;
import org.dromara.jpom.permission.*;
import org.dromara.jpom.service.dblog.BuildInfoService;
import org.dromara.jpom.service.dblog.DbBuildHistoryLogService;
import org.dromara.jpom.service.dblog.RepositoryService;
import org.dromara.jpom.service.monitor.MonitorService;
import org.dromara.jpom.service.node.ProjectInfoCacheService;
import org.dromara.jpom.service.outgiving.LogReadServer;
import org.dromara.jpom.service.outgiving.OutGivingServer;
import org.dromara.jpom.service.system.WhitelistDirectoryService;
import org.dromara.jpom.system.ServerConfig;
import org.springframework.http.MediaType;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.Reader;
import java.nio.charset.Charset;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* 项目管理
*
* @author bwcx_jzy
* @since 2018/9/29
*/
@RestController
@RequestMapping(value = "/node/manage/")
@Feature(cls = ClassFeature.PROJECT)
@NodeDataPermission(cls = ProjectInfoCacheService.class)
@Slf4j
public class ProjectManageControl extends BaseServerController {
private final OutGivingServer outGivingServer;
private final LogReadServer logReadServer;
private final MonitorService monitorService;
private final BuildInfoService buildService;
private final RepositoryService repositoryService;
private final ProjectInfoCacheService projectInfoCacheService;
private final DbBuildHistoryLogService dbBuildHistoryLogService;
private final ServerConfig serverConfig;
private final WhitelistDirectoryService whitelistDirectoryService;
public ProjectManageControl(OutGivingServer outGivingServer,
LogReadServer logReadServer,
MonitorService monitorService,
BuildInfoService buildService,
RepositoryService repositoryService,
ProjectInfoCacheService projectInfoCacheService,
DbBuildHistoryLogService dbBuildHistoryLogService,
ServerConfig serverConfig,
WhitelistDirectoryService whitelistDirectoryService) {
this.outGivingServer = outGivingServer;
this.logReadServer = logReadServer;
this.monitorService = monitorService;
this.buildService = buildService;
this.repositoryService = repositoryService;
this.projectInfoCacheService = projectInfoCacheService;
this.dbBuildHistoryLogService = dbBuildHistoryLogService;
this.serverConfig = serverConfig;
this.whitelistDirectoryService = whitelistDirectoryService;
}
private void checkProjectPermission(String id, HttpServletRequest request, NodeModel node) {
if (StrUtil.isEmpty(id)) {
return;
}
String workspaceId = projectInfoCacheService.getCheckUserWorkspace(request);
String fullId = ProjectInfoCacheModel.fullId(workspaceId, node.getId(), id);
boolean exists = projectInfoCacheService.exists(fullId);
if (!exists) {
// 判断如果项目 id 不存在则表示新增
ProjectInfoCacheModel projectInfoCacheModel = new ProjectInfoCacheModel();
projectInfoCacheModel.setProjectId(id);
projectInfoCacheModel.setNodeId(node.getId());
boolean exists1 = projectInfoCacheService.exists(projectInfoCacheModel);
if (!exists1) {
// 新增数据
return;
}
}
Assert.state(exists, I18nMessageUtil.get("i18n.no_corresponding_data_or_permission.1291"));
}
@RequestMapping(value = "getProjectData.json", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public IJsonMessage getProjectData(@ValidatorItem String id, HttpServletRequest request) {
NodeModel node = getNode();
this.checkProjectPermission(id, request, node);
JSONObject projectInfo = projectInfoCacheService.getItem(node, id);
return JsonMessage.success("", projectInfo);
}
/**
* get project access list
* 获取项目的授权
*
* @return json
* @author Hotstrip
*/
@RequestMapping(value = "project-access-list", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public IJsonMessage> projectAccessList() {
List jsonArray = whitelistDirectoryService.getProjectDirectory(getNode());
return JsonMessage.success("", jsonArray);
}
/**
* 保存项目
*
* @param id id
* @return json
*/
@RequestMapping(value = "saveProject", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
@Feature(method = MethodFeature.EDIT)
public IJsonMessage saveProject(String id, HttpServletRequest request) {
NodeModel node = getNode();
this.checkProjectPermission(id, request, node);
//
JsonMessage jsonMessage = NodeForward.request(node, request, NodeUrl.Manage_SaveProject, "outGivingProject");
if (jsonMessage.success()) {
projectInfoCacheService.syncNode(node, id);
}
return jsonMessage;
}
/**
* 释放分发
*
* @return json
*/
@RequestMapping(value = "release-outgiving", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
@Feature(method = MethodFeature.EDIT)
public IJsonMessage releaseOutgiving(String id, HttpServletRequest request) {
NodeModel node = getNode();
this.checkProjectPermission(id, request, node);
JsonMessage jsonMessage = NodeForward.request(getNode(), request, NodeUrl.Manage_ReleaseOutGiving);
if (jsonMessage.success()) {
projectInfoCacheService.syncNode(node, id);
}
return jsonMessage;
}
/**
* 获取正在运行的项目的端口和进程id
*
* @return json
*/
@RequestMapping(value = "getProjectPort", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public IJsonMessage getProjectPort(HttpServletRequest request) {
return NodeForward.request(getNode(), request, NodeUrl.Manage_GetProjectPort);
}
/**
* 查询所有项目
*
* @return json
*/
@PostMapping(value = "get_project_info", produces = MediaType.APPLICATION_JSON_VALUE)
@Feature(method = MethodFeature.LIST)
public IJsonMessage> getProjectInfo(HttpServletRequest request) {
PageResultDto modelPageResultDto = projectInfoCacheService.listPage(request);
return JsonMessage.success("", modelPageResultDto);
}
/**
* 删除项目
*
* @param id id
* @return json
*/
@PostMapping(value = "deleteProject", produces = MediaType.APPLICATION_JSON_VALUE)
@Feature(method = MethodFeature.DEL)
public IJsonMessage deleteProject(@ValidatorItem(value = ValidatorRule.NOT_BLANK) String id,
HttpServletRequest request) {
NodeModel nodeModel = getNode();
this.checkProjectPermission(id, request, nodeModel);
// 检查节点分发
outGivingServer.checkNodeProject(nodeModel.getId(), id, request, I18nMessageUtil.get("i18n.project_has_node_distribution_cannot_delete.41b0"));
// 检查日志阅读
logReadServer.checkNodeProject(nodeModel.getId(), id, request, I18nMessageUtil.get("i18n.project_has_logs_cannot_delete.1d2a"));
// 项目监控
List monitorModels = monitorService.listByWorkspace(request);
if (monitorModels != null) {
boolean match = monitorModels.stream().anyMatch(monitorModel -> monitorModel.checkNodeProject(nodeModel.getId(), id));
Assert.state(!match, I18nMessageUtil.get("i18n.project_has_monitoring_items_cannot_delete.c9a3"));
}
// 构建
boolean releaseMethod = buildService.checkReleaseMethod(nodeModel.getId() + StrUtil.COLON + id, request, BuildReleaseMethod.Project);
Assert.state(!releaseMethod, I18nMessageUtil.get("i18n.project_has_build_items_cannot_delete.c2df"));
JsonMessage jsonMessage = NodeForward.request(nodeModel, request, NodeUrl.Manage_DeleteProject);
if (jsonMessage.success()) {
//
projectInfoCacheService.syncExecuteNode(nodeModel);
}
return jsonMessage;
}
/**
* 查看项目关联在线构建的数据
*
* @param projectData 项目数据
* @param request 请求
* @param toWorkspaceId 工作空间ID
* @return list
*/
private List checkBuild(ProjectInfoCacheModel projectData, String toWorkspaceId, HttpServletRequest request) {
// 构建
String dataId = projectData.getNodeId() + StrUtil.COLON + projectData.getProjectId();
List buildInfoModels = buildService.listReleaseMethod(dataId, request, BuildReleaseMethod.Project);
if (buildInfoModels != null) {
return buildInfoModels.stream()
.map(buildInfoModel -> {
// 判断共享仓库
RepositoryModel repositoryModel = repositoryService.getByKey(buildInfoModel.getRepositoryId());
Assert.notNull(repositoryModel, I18nMessageUtil.get("i18n.repository_does_not_exist.3cdb"));
if (StrUtil.equals(repositoryModel.getWorkspaceId(), toWorkspaceId)) {
// 迁移前后是同一个工作空间
return null;
}
// 非全局仓库判断仓库关联的构建
if (!repositoryModel.global()) {
BuildInfoModel buildInfoModel1 = new BuildInfoModel();
buildInfoModel1.setRepositoryId(buildInfoModel.getRepositoryId());
buildInfoModel1.setWorkspaceId(projectData.getWorkspaceId());
List infoModels = buildService.listByBean(buildInfoModel1);
if (CollUtil.size(infoModels) > 1) {
// 判断如果使用通过一个仓库
long count = infoModels.stream()
.filter(buildInfoModel2 -> {
// 发布方式和数据id 不一样
return !StrUtil.equals(buildInfoModel2.getReleaseMethodDataId(), dataId);
})
.count();
Assert.state(count <= 0, StrUtil.format(I18nMessageUtil.get("i18n.current_project_associated_with_online_build_and_repository.96c5"), repositoryModel.getName(), count));
}
}
return new Tuple(buildInfoModel, repositoryModel);
})
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
return new ArrayList<>();
}
/**
* 迁移项目关联在线构建的数据
*
* @param list 构建数据
* @param toWorkspaceId 迁移到哪个工作空间
* @return list
*/
private String migrateBuild(List list, String toWorkspaceId, String toNodeId, ProjectInfoCacheModel projectData) {
return list.stream()
.map(tuple -> {
BuildInfoModel infoModel = tuple.get(0);
RepositoryModel repository = tuple.get(1);
if (!repository.global()) {
// 非全局仓库才 修改仓库所属工作空间
String repositoryId = infoModel.getRepositoryId();
RepositoryModel repositoryModel = new RepositoryModel();
repositoryModel.setId(repositoryId);
repositoryModel.setWorkspaceId(toWorkspaceId);
repositoryService.updateById(repositoryModel);
}
//
BuildInfoModel buildInfoModel = new BuildInfoModel();
buildInfoModel.setId(infoModel.getId());
buildInfoModel.setWorkspaceId(toWorkspaceId);
// 修改发布的关联数据
buildInfoModel.setReleaseMethodDataId(toNodeId + StrUtil.COLON + projectData.getProjectId());
buildService.updateById(buildInfoModel);
// 修改构建记录
dbBuildHistoryLogService.update(
Entity.create().set("workspaceId", toWorkspaceId),
Entity.create().set("buildDataId", infoModel.getId())
);
if (!repository.global()) {
return StrUtil.format(I18nMessageUtil.get("i18n.auto_migrate_associated_build_and_repo.0b3f"), infoModel.getName(), repository.getName());
}
return StrUtil.format(I18nMessageUtil.get("i18n.auto_migrate_associated_build.a060"), infoModel.getName());
}).
collect(Collectors.joining(" | "));
}
/**
* 迁移工作空间
*
* @param id id
* @return json
*/
@PostMapping(value = "migrate-workspace", produces = MediaType.APPLICATION_JSON_VALUE)
@Feature(method = MethodFeature.EDIT)
@SystemPermission
public IJsonMessage migrateWorkspace(@ValidatorItem(value = ValidatorRule.NOT_BLANK) String id,
@ValidatorItem(value = ValidatorRule.NOT_BLANK) String toWorkspaceId,
@ValidatorItem(value = ValidatorRule.NOT_BLANK) String toNodeId,
HttpServletRequest request) {
ProjectInfoCacheModel projectData = projectInfoCacheService.getByKey(id, request);
Assert.notNull(projectData, I18nMessageUtil.get("i18n.project_does_not_exist.3029"));
Assert.state(!StrUtil.equals(toWorkspaceId, projectData.getWorkspaceId()) || !StrUtil.equals(projectData.getNodeId(), toNodeId), I18nMessageUtil.get("i18n.target_workspace_consistency.e04c"));
projectInfoCacheService.checkUserWorkspace(toWorkspaceId);
//
NodeModel nowNode = nodeService.getByKey(projectData.getNodeId());
Assert.notNull(nowNode, I18nMessageUtil.get("i18n.corresponding_node_does_not_exist.72cb"));
NodeModel toNodeModel = nodeService.getByKey(toNodeId);
Assert.notNull(toNodeModel, I18nMessageUtil.get("i18n.node_not_exist.760e"));
Assert.state(StrUtil.equals(toWorkspaceId, toNodeModel.getWorkspaceId()), I18nMessageUtil.get("i18n.migration_target_workspace_node_mismatch.d9cf"));
// 检查节点分发
outGivingServer.checkNodeProject(projectData.getNodeId(), projectData.getProjectId(), request, I18nMessageUtil.get("i18n.project_has_node_distribution_cannot_migrate.cc0e"));
// 检查日志阅读
logReadServer.checkNodeProject(projectData.getNodeId(), projectData.getProjectId(), request, I18nMessageUtil.get("i18n.project_has_logs_cannot_migrate.2e0e"));
// 项目监控
List monitorModels = monitorService.listByWorkspace(request);
if (monitorModels != null) {
boolean match = monitorModels.stream().anyMatch(monitorModel -> monitorModel.checkNodeProject(projectData.getNodeId(), id));
Assert.state(!match, I18nMessageUtil.get("i18n.project_has_monitoring_items_cannot_migrate.c7f6"));
}
// 检查构建
List buildInfoModels = this.checkBuild(projectData, toWorkspaceId, request);
JsonMessage result;
if (StrUtil.equals(nowNode.getMachineId(), toNodeModel.getMachineId())) {
// 相同机器
JSONObject jsonObject = new JSONObject();
jsonObject.put("newWorkspaceId", toWorkspaceId);
jsonObject.put("newNodeId", toNodeId);
jsonObject.put("id", projectData.getProjectId());
JsonMessage jsonMessage = NodeForward.request(nowNode, NodeUrl.Manage_ChangeWorkspaceId, jsonObject);
if (!jsonMessage.success()) {
return new JsonMessage<>(406, nowNode.getName() + I18nMessageUtil.get("i18n.node_migration_project_failure.d5ff") + jsonMessage.getMsg());
}
result = jsonMessage;
} else {
JSONObject item = projectInfoCacheService.getItem(nowNode, projectData.getProjectId());
Assert.notNull(item, I18nMessageUtil.get("i18n.project_data_lost.2ae3"));
item = projectInfoCacheService.convertToRequestData(item);
item.put("nodeId", toNodeId);
item.put("workspaceId", toWorkspaceId);
item.put("previewData", true);
// 发起预检查数据
JsonMessage jsonMessage = NodeForward.request(toNodeModel, NodeUrl.Manage_SaveProject, item);
if (!jsonMessage.success()) {
return new JsonMessage<>(406, toNodeModel.getName() + I18nMessageUtil.get("i18n.node_and_check_project_failed.ac4b") + jsonMessage.getMsg());
}
item.remove("previewData");
jsonMessage = NodeForward.request(toNodeModel, NodeUrl.Manage_SaveProject, item);
if (!jsonMessage.success()) {
return new JsonMessage<>(406, toNodeModel.getName() + I18nMessageUtil.get("i18n.node_sync_project_failed.a2a7") + jsonMessage.getMsg());
}
// 删除之前节点项目
JSONObject delData = new JSONObject();
delData.put("id", projectData.getProjectId());
// 非强制
delData.put("thorough", "");
JsonMessage delJsonMeg = NodeForward.request(nowNode, NodeUrl.Manage_DeleteProject, delData);
if (!delJsonMeg.success()) {
return new JsonMessage<>(406, nowNode.getName() + I18nMessageUtil.get("i18n.node_delete_project_failed.534c") + delJsonMeg.getMsg());
}
result = jsonMessage;
}
// 迁移构建
String buildMsg = this.migrateBuild(buildInfoModels, toWorkspaceId, toNodeId, projectData);
// 刷新缓存
projectInfoCacheService.syncExecuteNode(nowNode);
projectInfoCacheService.syncExecuteNode(toNodeModel);
return new JsonMessage<>(200, StrUtil.format(I18nMessageUtil.get("i18n.migration_success_message.e546"), result.getMsg(), buildMsg));
}
/**
* 操作项目
*
* nodeId,id
*
* @return json
*/
@RequestMapping(value = "operate", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
@Feature(method = MethodFeature.EXECUTE)
public IJsonMessage
© 2015 - 2024 Weber Informatics LLC | Privacy Policy