系统未初始化提示,新增清空项目缓存功能,页面分页bug,只有超级管理员才能修改超级管理员的信息,新增数据默认工作空间问题,同步项目缓存项目工作空间问题

This commit is contained in:
bwcx_jzy 2021-12-08 17:11:47 +08:00
parent 3dec2ce572
commit 83276eca54
29 changed files with 350 additions and 107 deletions

View File

@ -45,6 +45,8 @@
> 6: 用户绑定工作空间后,用户在对应工作空间下可以创建、修改、删除对应的数据(包括但不限于管理节点) > 6: 用户绑定工作空间后,用户在对应工作空间下可以创建、修改、删除对应的数据(包括但不限于管理节点)
> >
> 7: 此次升级启动耗时可能需要2分钟以上耗时根据数据量来决定请耐心等待和观察控制台日志输出 > 7: 此次升级启动耗时可能需要2分钟以上耗时根据数据量来决定请耐心等待和观察控制台日志输出
>
> 8: 一个节点不要被多个服务端绑定
------ ------
# 2.7.3 # 2.7.3

View File

@ -1,3 +1,26 @@
#
# 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.
#
FROM centos:7 FROM centos:7
ENV LANG en_US.utf8 ENV LANG en_US.utf8

View File

@ -48,7 +48,6 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.io.File; import java.io.File;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.ArrayList; import java.util.ArrayList;
@ -63,10 +62,15 @@ import java.util.List;
@RestController @RestController
@RequestMapping(value = "/manage/") @RequestMapping(value = "/manage/")
public class ManageEditProjectController extends BaseAgentController { public class ManageEditProjectController extends BaseAgentController {
@Resource
private WhitelistDirectoryService whitelistDirectoryService; private final WhitelistDirectoryService whitelistDirectoryService;
@Resource private final JdkInfoService jdkInfoService;
private JdkInfoService jdkInfoService;
public ManageEditProjectController(WhitelistDirectoryService whitelistDirectoryService,
JdkInfoService jdkInfoService) {
this.whitelistDirectoryService = whitelistDirectoryService;
this.jdkInfoService = jdkInfoService;
}
/** /**
* 基础检查 * 基础检查

View File

@ -1,3 +1,26 @@
#
# 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.
#
FROM centos:7 FROM centos:7
ENV LANG en_US.utf8 ENV LANG en_US.utf8

View File

@ -66,13 +66,14 @@ public class GlobalDefaultExceptionHandler {
*/ */
@ExceptionHandler({AuthorizeException.class, RuntimeException.class, Exception.class}) @ExceptionHandler({AuthorizeException.class, RuntimeException.class, Exception.class})
public void delExceptionHandler(HttpServletRequest request, HttpServletResponse response, Exception e) { public void delExceptionHandler(HttpServletRequest request, HttpServletResponse response, Exception e) {
DefaultSystemLog.getLog().error("global handle exception: {}", request.getRequestURI(), e);
if (e instanceof AuthorizeException) { if (e instanceof AuthorizeException) {
AuthorizeException authorizeException = (AuthorizeException) e; AuthorizeException authorizeException = (AuthorizeException) e;
ServletUtil.write(response, authorizeException.getJsonMessage().toString(), MediaType.APPLICATION_JSON_VALUE); ServletUtil.write(response, authorizeException.getJsonMessage().toString(), MediaType.APPLICATION_JSON_VALUE);
} else if (e instanceof JpomRuntimeException) { } else if (e instanceof JpomRuntimeException) {
DefaultSystemLog.getLog().error("global handle exception: {}", request.getRequestURI(), e.getCause());
ServletUtil.write(response, JsonMessage.getString(500, e.getMessage()), MediaType.APPLICATION_JSON_VALUE); ServletUtil.write(response, JsonMessage.getString(500, e.getMessage()), MediaType.APPLICATION_JSON_VALUE);
} else { } else {
DefaultSystemLog.getLog().error("global handle exception: {}", request.getRequestURI(), e);
boolean causedBy = ExceptionUtil.isCausedBy(e, AccessDeniedException.class); boolean causedBy = ExceptionUtil.isCausedBy(e, AccessDeniedException.class);
if (causedBy) { if (causedBy) {
ServletUtil.write(response, JsonMessage.getString(500, "操作文件权限异常,请手动处理:" + e.getMessage()), MediaType.APPLICATION_JSON_VALUE); ServletUtil.write(response, JsonMessage.getString(500, "操作文件权限异常,请手动处理:" + e.getMessage()), MediaType.APPLICATION_JSON_VALUE);

View File

@ -34,6 +34,7 @@ import io.jpom.model.data.UserModel;
import io.jpom.service.user.UserService; import io.jpom.service.user.UserService;
import io.jpom.system.ServerConfigBean; import io.jpom.system.ServerConfigBean;
import io.jpom.system.ServerExtConfigBean; import io.jpom.system.ServerExtConfigBean;
import io.jpom.system.db.DbConfig;
import io.jpom.util.JwtUtil; import io.jpom.util.JwtUtil;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.web.method.HandlerMethod; import org.springframework.web.method.HandlerMethod;
@ -60,6 +61,11 @@ public class LoginInterceptor extends BaseJpomInterceptor {
@Override @Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { public boolean preHandle(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
HttpSession session = getSession(); HttpSession session = getSession();
boolean init = DbConfig.getInstance().isInit();
if (!init) {
ServletUtil.write(response, JsonMessage.getString(100, "数据库还没有初始化成功,请耐心等待"), MediaType.APPLICATION_JSON_VALUE);
return false;
}
// //
NotLogin notLogin = handlerMethod.getMethodAnnotation(NotLogin.class); NotLogin notLogin = handlerMethod.getMethodAnnotation(NotLogin.class);
if (notLogin == null) { if (notLogin == null) {

View File

@ -78,6 +78,7 @@ public class InstallController extends BaseServerController {
@ValidatorConfig(value = { @ValidatorConfig(value = {
@ValidatorItem(value = ValidatorRule.NOT_BLANK, msg = "密码不能为空") @ValidatorItem(value = ValidatorRule.NOT_BLANK, msg = "密码不能为空")
}) String userPwd) { }) String userPwd) {
//
Assert.state(!userService.canUse(), "系统已经初始化过啦,请勿重复初始化"); Assert.state(!userService.canUse(), "系统已经初始化过啦,请勿重复初始化");
if (JpomApplication.SYSTEM_ID.equalsIgnoreCase(userName) || UserModel.SYSTEM_ADMIN.equals(userName)) { if (JpomApplication.SYSTEM_ID.equalsIgnoreCase(userName) || UserModel.SYSTEM_ADMIN.equals(userName)) {

View File

@ -231,7 +231,7 @@ public class RepositoryController extends BaseServerController {
boolean exists = buildInfoService.exists(entity); boolean exists = buildInfoService.exists(entity);
Assert.state(!exists, "当前仓库被构建关联,不能直接删除"); Assert.state(!exists, "当前仓库被构建关联,不能直接删除");
repositoryService.getByKey(id, getRequest()); repositoryService.delByKey(id, getRequest());
return JsonMessage.getString(200, "删除成功"); return JsonMessage.getString(200, "删除成功");
} }
} }

View File

@ -20,6 +20,7 @@ import org.springframework.http.MediaType;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.util.List; import java.util.List;
/** /**
@ -68,10 +69,12 @@ public class NodeEditController extends BaseServerController {
@Feature(method = MethodFeature.LIST) @Feature(method = MethodFeature.LIST)
public String nodeStatus() { public String nodeStatus() {
long timeMillis = System.currentTimeMillis(); long timeMillis = System.currentTimeMillis();
JSONObject jsonObject = NodeForward.requestData(getNode(), NodeUrl.Status, getRequest(), JSONObject.class); NodeModel node = getNode();
JSONObject jsonObject = NodeForward.requestData(node, NodeUrl.Status, getRequest(), JSONObject.class);
Assert.notNull(jsonObject, "获取信息失败"); Assert.notNull(jsonObject, "获取信息失败");
JSONArray jsonArray = new JSONArray(); JSONArray jsonArray = new JSONArray();
jsonObject.put("timeOut", System.currentTimeMillis() - timeMillis); jsonObject.put("timeOut", System.currentTimeMillis() - timeMillis);
jsonObject.put("nodeId", node.getId());
jsonArray.add(jsonObject); jsonArray.add(jsonObject);
return JsonMessage.getString(200, "", jsonArray); return JsonMessage.getString(200, "", jsonArray);
} }
@ -94,7 +97,8 @@ public class NodeEditController extends BaseServerController {
@Feature(method = MethodFeature.DEL) @Feature(method = MethodFeature.DEL)
public String del(String id) { public String del(String id) {
// 判断分发 // 判断分发
boolean checkNode = outGivingServer.checkNode(id, getRequest()); HttpServletRequest request = getRequest();
boolean checkNode = outGivingServer.checkNode(id, request);
Assert.state(!checkNode, "该节点存在分发项目,不能删除"); Assert.state(!checkNode, "该节点存在分发项目,不能删除");
// 监控 // 监控
boolean checkNode1 = monitorService.checkNode(id); boolean checkNode1 = monitorService.checkNode(id);
@ -104,9 +108,10 @@ public class NodeEditController extends BaseServerController {
// //
ProjectInfoModel projectInfoModel = new ProjectInfoModel(); ProjectInfoModel projectInfoModel = new ProjectInfoModel();
projectInfoModel.setNodeId(id); projectInfoModel.setNodeId(id);
projectInfoModel.setWorkspaceId(projectInfoCacheService.getCheckUserWorkspace(request));
boolean exists = projectInfoCacheService.exists(projectInfoModel); boolean exists = projectInfoCacheService.exists(projectInfoModel);
Assert.state(!exists, "该节点下还存在项目,不能删除"); Assert.state(!exists, "该节点下还存在项目,不能删除");
nodeService.delByKey(id, getRequest()); nodeService.delByKey(id, request);
return JsonMessage.getString(200, "操作成功"); return JsonMessage.getString(200, "操作成功");
} }
} }

View File

@ -1,13 +1,18 @@
package io.jpom.controller.node; package io.jpom.controller.node;
import cn.hutool.db.Entity;
import cn.jiangzeyin.common.JsonMessage; import cn.jiangzeyin.common.JsonMessage;
import io.jpom.common.BaseServerController; import io.jpom.common.BaseServerController;
import io.jpom.model.PageResultDto; import io.jpom.model.PageResultDto;
import io.jpom.model.data.NodeModel;
import io.jpom.model.data.ProjectInfoModel; import io.jpom.model.data.ProjectInfoModel;
import io.jpom.permission.SystemPermission;
import io.jpom.plugin.ClassFeature; import io.jpom.plugin.ClassFeature;
import io.jpom.plugin.Feature; import io.jpom.plugin.Feature;
import io.jpom.plugin.MethodFeature;
import io.jpom.service.node.ProjectInfoCacheService; import io.jpom.service.node.ProjectInfoCacheService;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
@ -70,4 +75,47 @@ public class NodeProjectInfoController extends BaseServerController {
List<ProjectInfoModel> projectInfoModels = projectInfoCacheService.listByWorkspace(getRequest()); List<ProjectInfoModel> projectInfoModels = projectInfoCacheService.listByWorkspace(getRequest());
return JsonMessage.getString(200, "", projectInfoModels); return JsonMessage.getString(200, "", projectInfoModels);
} }
/**
* 同步节点项目
*
* @return json
*/
@GetMapping(value = "sync_project", produces = MediaType.APPLICATION_JSON_VALUE)
public String syncProject(String nodeId) {
NodeModel nodeModel = nodeService.getByKey(nodeId);
Assert.notNull(nodeModel, "对应的节点不存在");
String msg = projectInfoCacheService.syncExecuteNode(nodeModel);
return JsonMessage.getString(200, msg);
}
/**
* 删除节点缓存的项目信息
*
* @return json
*/
@GetMapping(value = "del_project_cache", produces = MediaType.APPLICATION_JSON_VALUE)
public String delProjectCache(String nodeId) {
NodeModel nodeModel = nodeService.getByKey(nodeId);
Assert.notNull(nodeModel, "对应的节点不存在");
int count = projectInfoCacheService.delProjectCache(nodeId, getRequest());
return JsonMessage.getString(200, "成功删除" + count + "条项目缓存");
}
/**
* 删除节点缓存的所有项目
*
* @return json
*/
@GetMapping(value = "clear_all_project", produces = MediaType.APPLICATION_JSON_VALUE)
@SystemPermission(superUser = true)
@Feature(method = MethodFeature.DEL)
public String clearAll() {
Entity where = Entity.create();
where.set("id", " <> id");
int del = projectInfoCacheService.del(where);
return JsonMessage.getString(200, "成功删除" + del + "条项目缓存");
}
} }

View File

@ -158,7 +158,16 @@ public class SshController extends BaseServerController {
boolean exists = sshService.exists(entity); boolean exists = sshService.exists(entity);
Assert.state(!exists, "对应的SSH已经存在啦"); Assert.state(!exists, "对应的SSH已经存在啦");
try { try {
Session session = add ? SshService.getSessionByModel(sshModel) : SshService.getSession(id); SshModel model = sshService.getByKey(id, false);
if (model != null) {
if (StrUtil.isEmpty(sshModel.getPassword())) {
sshModel.setPassword(model.getPassword());
}
if (StrUtil.isEmpty(sshModel.getPrivateKey())) {
sshModel.setPrivateKey(model.getPrivateKey());
}
}
Session session = SshService.getSessionByModel(sshModel);
JschUtil.close(session); JschUtil.close(session);
} catch (Exception e) { } catch (Exception e) {
return JsonMessage.getString(505, "ssh连接失败" + e.getMessage()); return JsonMessage.getString(505, "ssh连接失败" + e.getMessage());
@ -181,7 +190,7 @@ public class SshController extends BaseServerController {
@GetMapping(value = "check_agent.json", produces = MediaType.APPLICATION_JSON_VALUE) @GetMapping(value = "check_agent.json", produces = MediaType.APPLICATION_JSON_VALUE)
@Feature(method = MethodFeature.LIST) @Feature(method = MethodFeature.LIST)
public String checkAgent(String ids) { public String checkAgent(String ids) {
List<SshModel> sshModels = sshService.listById(StrUtil.split(ids, StrUtil.DOT), getRequest()); List<SshModel> sshModels = sshService.listById(StrUtil.split(ids, StrUtil.COMMA), getRequest());
Assert.notEmpty(sshModels, "没有任何节点信息"); Assert.notEmpty(sshModels, "没有任何节点信息");
JSONObject result = new JSONObject(); JSONObject result = new JSONObject();

View File

@ -369,7 +369,7 @@ public class OutGivingProjectEditController extends BaseServerController {
allData.put("args_" + copyId, copyArgs); allData.put("args_" + copyId, copyArgs);
} }
} }
JsonMessage<String> jsonMessage = sendData(nodeModel, userModel, allData, false); JsonMessage<String> jsonMessage = this.sendData(nodeModel, userModel, allData, false);
Assert.state(jsonMessage.getCode() == HttpStatus.HTTP_OK, nodeModel.getName() + "节点失败:" + jsonMessage.getMsg()); Assert.state(jsonMessage.getCode() == HttpStatus.HTTP_OK, nodeModel.getName() + "节点失败:" + jsonMessage.getMsg());
tuples.add(new Tuple(nodeModel, allData)); tuples.add(new Tuple(nodeModel, allData));
} }

View File

@ -114,6 +114,13 @@ public class UserListController extends BaseServerController {
if (!systemUser) { if (!systemUser) {
Assert.state(!model.isSuperSystemUser(), "不能取消超级管理员的权限"); Assert.state(!model.isSuperSystemUser(), "不能取消超级管理员的权限");
} }
UserModel optUser = getUser();
if (StrUtil.equals(model.getId(), optUser.getId())) {
Assert.state(model.isSystemUser() == optUser.isSystemUser(), "不能修改自己的管理员权限");
}
if (model.isSuperSystemUser()) {
Assert.state(optUser.isSuperSystemUser(), "超级管理员才能修改超级管理员的信息");
}
userService.update(userModel); userService.update(userModel);
} }
// //

View File

@ -39,10 +39,8 @@ import io.jpom.common.BaseServerController;
import io.jpom.common.Const; import io.jpom.common.Const;
import io.jpom.model.BaseDbModel; import io.jpom.model.BaseDbModel;
import io.jpom.model.BaseUserModifyDbModel; import io.jpom.model.BaseUserModifyDbModel;
import io.jpom.model.BaseWorkspaceModel;
import io.jpom.model.PageResultDto; import io.jpom.model.PageResultDto;
import io.jpom.model.data.UserModel; import io.jpom.model.data.UserModel;
import io.jpom.model.data.WorkspaceModel;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -124,11 +122,6 @@ public abstract class BaseDbService<T extends BaseDbModel> extends BaseDbCommonS
modifyDbModel.setModifyUser(ObjectUtil.defaultIfNull(modifyDbModel.getModifyUser(), userModel.getId())); modifyDbModel.setModifyUser(ObjectUtil.defaultIfNull(modifyDbModel.getModifyUser(), userModel.getId()));
} }
} }
if (t instanceof BaseWorkspaceModel) {
// 数据都将绑定一个默认的工作空间
BaseWorkspaceModel baseWorkspaceModel = (BaseWorkspaceModel) t;
baseWorkspaceModel.setWorkspaceId(ObjectUtil.defaultIfNull(baseWorkspaceModel.getWorkspaceId(), WorkspaceModel.DEFAULT_ID));
}
} }
/** /**

View File

@ -33,8 +33,10 @@ import io.jpom.model.BaseWorkspaceModel;
import io.jpom.model.PageResultDto; import io.jpom.model.PageResultDto;
import io.jpom.model.data.UserBindWorkspaceModel; import io.jpom.model.data.UserBindWorkspaceModel;
import io.jpom.model.data.UserModel; import io.jpom.model.data.UserModel;
import io.jpom.model.data.WorkspaceModel;
import io.jpom.service.user.UserBindWorkspaceService; import io.jpom.service.user.UserBindWorkspaceService;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import java.util.Collection; import java.util.Collection;
@ -93,9 +95,14 @@ public abstract class BaseWorkspaceService<T extends BaseWorkspaceModel> extends
super.fillInsert(t); super.fillInsert(t);
if (StrUtil.isEmpty(t.getWorkspaceId())) { if (StrUtil.isEmpty(t.getWorkspaceId())) {
// 自动绑定 工作空间ID // 自动绑定 工作空间ID
HttpServletRequest request = BaseServerController.getRequestAttributes().getRequest(); ServletRequestAttributes servletRequestAttributes = BaseServerController.tryGetRequestAttributes();
String workspaceId = getCheckUserWorkspace(request); if (servletRequestAttributes != null) {
t.setWorkspaceId(workspaceId); HttpServletRequest request = servletRequestAttributes.getRequest();
String workspaceId = getCheckUserWorkspace(request);
t.setWorkspaceId(workspaceId);
} else {
t.setWorkspaceId(WorkspaceModel.DEFAULT_ID);
}
} else { } else {
// 检查权限 // 检查权限
this.checkUserWorkspace(t.getWorkspaceId()); this.checkUserWorkspace(t.getWorkspaceId());

View File

@ -14,7 +14,6 @@ import io.jpom.model.data.WorkspaceModel;
import io.jpom.monitor.NodeMonitor; import io.jpom.monitor.NodeMonitor;
import io.jpom.service.h2db.BaseWorkspaceService; import io.jpom.service.h2db.BaseWorkspaceService;
import io.jpom.service.node.ssh.SshService; import io.jpom.service.node.ssh.SshService;
import io.jpom.util.StringUtil;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.Assert; import org.springframework.util.Assert;
@ -56,8 +55,8 @@ public class NodeService extends BaseWorkspaceService<NodeModel> {
NodeModel nodeModel = ServletUtil.toBean(request, NodeModel.class, true); NodeModel nodeModel = ServletUtil.toBean(request, NodeModel.class, true);
String id = nodeModel.getId(); String id = nodeModel.getId();
if (StrUtil.isNotEmpty(id)) { if (StrUtil.isNotEmpty(id)) {
boolean general = StringUtil.isGeneral(id, 2, 20); //boolean general = StringUtil.isGeneral(id, 2, 20);
Assert.state(general, "节点id不能为空并且2-20英文字母 、数字和下划线)"); //Assert.state(general, "节点id不能为空并且2-20英文字母 、数字和下划线)");
} }
Assert.hasText(nodeModel.getName(), "节点名称 不能为空"); Assert.hasText(nodeModel.getName(), "节点名称 不能为空");
// 节点地址 重复 // 节点地址 重复

View File

@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.thread.ThreadUtil; import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.db.Entity;
import cn.jiangzeyin.common.DefaultSystemLog; import cn.jiangzeyin.common.DefaultSystemLog;
import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
@ -18,6 +19,7 @@ import io.jpom.service.h2db.BaseNodeService;
import io.jpom.service.system.WorkspaceService; import io.jpom.service.system.WorkspaceService;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@ -92,60 +94,71 @@ public class ProjectInfoCacheService extends BaseNodeService<ProjectInfoModel> {
* @param nodeModel 节点 * @param nodeModel 节点
*/ */
public void syncNode(final NodeModel nodeModel) { public void syncNode(final NodeModel nodeModel) {
ThreadUtil.execute(() -> this.syncExecuteNode(nodeModel));
}
/**
* 同步执行 同步节点项目信息
*
* @param nodeModel 节点信息
* @return json
*/
public String syncExecuteNode(NodeModel nodeModel) {
String nodeModelName = nodeModel.getName(); String nodeModelName = nodeModel.getName();
if (!nodeModel.isOpenStatus()) { if (!nodeModel.isOpenStatus()) {
DefaultSystemLog.getLog().debug("{} 节点未启用", nodeModelName); DefaultSystemLog.getLog().debug("{} 节点未启用", nodeModelName);
return; return "节点未启用";
} }
ThreadUtil.execute(() -> { try {
try { JSONArray jsonArray = NodeForward.requestData(nodeModel, NodeUrl.Manage_GetProjectInfo, JSONArray.class, "notStatus", "true");
JSONArray jsonArray = NodeForward.requestData(nodeModel, NodeUrl.Manage_GetProjectInfo, JSONArray.class, "notStatus", "true"); if (CollUtil.isEmpty(jsonArray)) {
if (CollUtil.isEmpty(jsonArray)) { DefaultSystemLog.getLog().debug("{} 节点没有拉取到任何项目项目", nodeModelName);
DefaultSystemLog.getLog().debug("{} 节点没有拉取到任何项目项目", nodeModelName); return "节点没有拉取到任何项目项目";
return;
}
// 查询现在存在的项目
ProjectInfoModel where = new ProjectInfoModel();
where.setWorkspaceId(nodeModel.getWorkspaceId());
where.setNodeId(nodeModel.getId());
List<ProjectInfoModel> projectInfoModelsCacheAll = super.listByBean(where);
projectInfoModelsCacheAll = ObjectUtil.defaultIfNull(projectInfoModelsCacheAll, Collections.EMPTY_LIST);
Set<String> cacheIds = projectInfoModelsCacheAll.stream()
.map(ProjectInfoModel::getProjectId)
.collect(Collectors.toSet());
//
List<ProjectInfoModel> projectInfoModels = jsonArray.toJavaList(ProjectInfoModel.class);
List<ProjectInfoModel> models = projectInfoModels.stream().peek(projectInfoModel -> {
projectInfoModel.setProjectId(projectInfoModel.getId());
projectInfoModel.setId(null);
projectInfoModel.setNodeId(nodeModel.getId());
if (StrUtil.isEmpty(projectInfoModel.getWorkspaceId())) {
projectInfoModel.setWorkspaceId(nodeModel.getWorkspaceId());
}
projectInfoModel.setId(projectInfoModel.fullId());
}).filter(projectInfoModel -> {
// 检查对应的工作空间 是否存在
return workspaceService.exists(new WorkspaceModel(projectInfoModel.getWorkspaceId()));
}).peek(projectInfoModel -> cacheIds.remove(projectInfoModel.getProjectId())).collect(Collectors.toList());
// 设置 临时缓存便于放行检查
BaseServerController.resetInfo(UserModel.EMPTY);
//
models.forEach(ProjectInfoCacheService.super::upsert);
// 删除项目好的
Set<String> strings = cacheIds.stream()
.map(s -> ProjectInfoModel.fullId(nodeModel.getWorkspaceId(), nodeModel.getId(), s))
.collect(Collectors.toSet());
if (CollUtil.isNotEmpty(strings)) {
super.delByKey(strings, null);
}
DefaultSystemLog.getLog().debug("{} 节点拉取到 {} 个项目,缓存 {} 个项目,删除 {} 个缓存",
nodeModelName, CollUtil.size(jsonArray), CollUtil.size(projectInfoModelsCacheAll), CollUtil.size(strings));
} catch (Exception e) {
DefaultSystemLog.getLog().error("同步节点项目失败:" + nodeModelName, e);
} finally {
BaseServerController.remove();
} }
}); // 查询现在存在的项目
ProjectInfoModel where = new ProjectInfoModel();
where.setWorkspaceId(nodeModel.getWorkspaceId());
where.setNodeId(nodeModel.getId());
List<ProjectInfoModel> projectInfoModelsCacheAll = super.listByBean(where);
projectInfoModelsCacheAll = ObjectUtil.defaultIfNull(projectInfoModelsCacheAll, Collections.EMPTY_LIST);
Set<String> cacheIds = projectInfoModelsCacheAll.stream()
.map(ProjectInfoModel::getProjectId)
.collect(Collectors.toSet());
//
List<ProjectInfoModel> projectInfoModels = jsonArray.toJavaList(ProjectInfoModel.class);
List<ProjectInfoModel> models = projectInfoModels.stream().peek(projectInfoModel -> {
projectInfoModel.setProjectId(projectInfoModel.getId());
projectInfoModel.setId(null);
projectInfoModel.setNodeId(nodeModel.getId());
if (StrUtil.isEmpty(projectInfoModel.getWorkspaceId())) {
projectInfoModel.setWorkspaceId(WorkspaceModel.DEFAULT_ID);
}
projectInfoModel.setId(projectInfoModel.fullId());
}).filter(projectInfoModel -> {
// 检查对应的工作空间 是否存在
return workspaceService.exists(new WorkspaceModel(projectInfoModel.getWorkspaceId()));
}).peek(projectInfoModel -> cacheIds.remove(projectInfoModel.getProjectId())).collect(Collectors.toList());
// 设置 临时缓存便于放行检查
BaseServerController.resetInfo(UserModel.EMPTY);
//
models.forEach(ProjectInfoCacheService.super::upsert);
// 删除项目
Set<String> strings = cacheIds.stream()
.map(s -> ProjectInfoModel.fullId(nodeModel.getWorkspaceId(), nodeModel.getId(), s))
.collect(Collectors.toSet());
if (CollUtil.isNotEmpty(strings)) {
super.delByKey(strings, null);
}
String format = StrUtil.format("{} 节点拉取到 {} 个项目,已经缓存 {} 个项目,更新 {} 个项目,删除 {} 个缓存",
nodeModelName, CollUtil.size(jsonArray), CollUtil.size(projectInfoModelsCacheAll), CollUtil.size(models), CollUtil.size(strings));
DefaultSystemLog.getLog().debug(format);
return format;
} catch (Exception e) {
DefaultSystemLog.getLog().error("同步节点项目失败:" + nodeModelName, e);
return "同步节点项目失败" + e.getMessage();
} finally {
BaseServerController.remove();
}
} }
/** /**
@ -179,6 +192,15 @@ public class ProjectInfoCacheService extends BaseNodeService<ProjectInfoModel> {
}); });
} }
public int delProjectCache(String nodeId, HttpServletRequest request) {
String checkUserWorkspace = this.getCheckUserWorkspace(request);
Entity entity = Entity.create();
entity.set("nodeId", nodeId);
entity.set("workspaceId", checkUserWorkspace);
return super.del(entity);
}
private void fullData(ProjectInfoModel projectInfoModel, NodeModel nodeModel) { private void fullData(ProjectInfoModel projectInfoModel, NodeModel nodeModel) {
projectInfoModel.setProjectId(projectInfoModel.getId()); projectInfoModel.setProjectId(projectInfoModel.getId());
projectInfoModel.setId(null); projectInfoModel.setId(null);

View File

@ -99,6 +99,7 @@ public class InitDb implements DisposableBean, InitializingBean {
*/ */
String[] files = new String[]{ String[] files = new String[]{
"classpath:/bin/h2-db-v1.sql", "classpath:/bin/h2-db-v1.sql",
"classpath:/bin/h2-db-v1.1.sql",
"classpath:/bin/h2-db-v2.sql", "classpath:/bin/h2-db-v2.sql",
"classpath:/bin/h2-db-v3.sql", "classpath:/bin/h2-db-v3.sql",
}; };

View File

@ -0,0 +1,6 @@
-- @author bwcx_jzy
ALTER TABLE USEROPERATELOGV1 ALTER COLUMN NODEID VARCHAR(50) COMMENT '节点ID';
ALTER TABLE MONITORNOTIFYLOG ALTER COLUMN NODEID VARCHAR(50) COMMENT '节点ID';

View File

@ -20,7 +20,7 @@ export default {
created() { created() {
this.$notification.config({ this.$notification.config({
top: "100px", top: "100px",
duration: 2, duration: 4,
}); });
}, },
}; };

View File

@ -14,7 +14,6 @@ export function getDishPatchListAll() {
return axios({ return axios({
url: "/outgiving/dispatch-list-all", url: "/outgiving/dispatch-list-all",
method: "get", method: "get",
}); });
} }

View File

@ -62,6 +62,33 @@ export function getProjectListAll() {
}); });
} }
// 同步节点项目
export function syncProject(nodeId) {
return axios({
url: "/node/sync_project",
method: "get",
params: { nodeId: nodeId },
});
}
// 删除节点项目缓存
export function delProjectCache(nodeId) {
return axios({
url: "/node/del_project_cache",
method: "get",
params: { nodeId: nodeId },
});
}
// 删除节点项目缓存
export function delAllProjectCache() {
return axios({
url: "/node/clear_all_project",
method: "get",
params: {},
});
}
/** /**
* 编辑 node * 编辑 node
* @param { * @param {

View File

@ -136,7 +136,7 @@ export default {
geteBuildHistory(this.listQuery).then((res) => { geteBuildHistory(this.listQuery).then((res) => {
if (res.code === 200) { if (res.code === 200) {
this.list = res.data.result; this.list = res.data.result;
this.total = res.data.total; this.listQuery.total = res.data.total;
} }
this.loading = false; this.loading = false;
}); });

View File

@ -598,7 +598,7 @@ export default {
}; };
// javaCopyItemList // javaCopyItemList
this.nodeList.forEach((node) => { this.nodeList.forEach((node) => {
this.temp[`${node.key}_javaCopyItemList`] = []; this.temp[`${node.id}_javaCopyItemList`] = [];
}); });
this.loadAccesList(); this.loadAccesList();

View File

@ -10,9 +10,6 @@
</div> </div>
<!-- 表格 :scroll="{ x: 1070, y: tableHeight -60 }" scroll expandedRowRender 不兼容没法同时使用不然会多出一行数据--> <!-- 表格 :scroll="{ x: 1070, y: tableHeight -60 }" scroll expandedRowRender 不兼容没法同时使用不然会多出一行数据-->
<a-table :loading="loading" :columns="columns" :data-source="list" bordered rowKey="id" @expand="expand" :pagination="(this, pagination)" @change="changePage"> <a-table :loading="loading" :columns="columns" :data-source="list" bordered rowKey="id" @expand="expand" :pagination="(this, pagination)" @change="changePage">
<a-tooltip slot="group" slot-scope="text" placement="topLeft" :title="text">
<span>{{ text }}</span>
</a-tooltip>
<a-tooltip slot="url" slot-scope="text" placement="topLeft" :title="text"> <a-tooltip slot="url" slot-scope="text" placement="topLeft" :title="text">
<span>{{ text }}</span> <span>{{ text }}</span>
</a-tooltip> </a-tooltip>
@ -35,6 +32,14 @@
<a-tooltip slot="runTime" slot-scope="text" placement="topLeft" :title="text"> <a-tooltip slot="runTime" slot-scope="text" placement="topLeft" :title="text">
<span>{{ text }}</span> <span>{{ text }}</span>
</a-tooltip> </a-tooltip>
<template slot="projectCount" slot-scope="text, item">
<a-tooltip placement="topLeft" :title="节点中的所有项目数量">
<a-tag @click="syncNode(item)">{{ text }} </a-tag>
</a-tooltip>
<a-tooltip placement="topLeft" :title="清除服务端缓存节点项目信息">
<a-icon @click="delNodeCache(item)" type="delete" />
</a-tooltip>
</template>
</a-table> </a-table>
</a-table> </a-table>
<!-- 编辑区 --> <!-- 编辑区 -->
@ -120,7 +125,7 @@
</template> </template>
<script> <script>
import { mapGetters } from "vuex"; import { mapGetters } from "vuex";
import { getNodeList, getNodeStatus, editNode, deleteNode } from "@/api/node"; import { getNodeList, getNodeStatus, editNode, deleteNode, syncProject, delProjectCache } from "@/api/node";
import { getSshListAll } from "@/api/ssh"; import { getSshListAll } from "@/api/ssh";
import NodeLayout from "./node-layout"; import NodeLayout from "./node-layout";
import Terminal from "./terminal"; import Terminal from "./terminal";
@ -168,12 +173,12 @@ export default {
], ],
childColumns: [ childColumns: [
{ title: "系统名", dataIndex: "osName", key: "osName", width: 100, ellipsis: true, scopedSlots: { customRender: "osName" } }, { title: "系统名", dataIndex: "osName", key: "osName", width: 100, ellipsis: true, scopedSlots: { customRender: "osName" } },
{ title: "JDK 版本", dataIndex: "javaVersion", key: "javaVersion", width: 120, ellipsis: true, scopedSlots: { customRender: "javaVersion" } }, { title: "JDK 版本", dataIndex: "javaVersion", key: "javaVersion", ellipsis: true, scopedSlots: { customRender: "javaVersion" } },
{ title: "JVM 总内存", dataIndex: "totalMemory", key: "totalMemory", width: 150 }, { title: "JVM 总内存", dataIndex: "totalMemory", key: "totalMemory", width: 120 },
{ title: "JVM 剩余内存", dataIndex: "freeMemory", key: "freeMemory", width: 180 }, { title: "JVM 剩余内存", dataIndex: "freeMemory", key: "freeMemory", width: 140 },
{ title: "Jpom 版本", dataIndex: "jpomVersion", key: "jpomVersion", width: 140 }, { title: "Jpom 版本", dataIndex: "jpomVersion", key: "jpomVersion", width: 120 },
{ title: "Java 程序数", dataIndex: "javaVirtualCount", key: "javaVirtualCount", width: 150 }, { title: "Java 程序数", dataIndex: "javaVirtualCount", key: "javaVirtualCount", width: 120 },
{ title: "项目数", dataIndex: "count", key: "count", width: 100 }, { title: "项目数", dataIndex: "count", key: "count", width: 90, scopedSlots: { customRender: "projectCount" } },
{ title: "响应时间", dataIndex: "timeOut", key: "timeOut", width: 120 }, { title: "响应时间", dataIndex: "timeOut", key: "timeOut", width: 120 },
{ title: "已运行时间", dataIndex: "runTime", key: "runTime", width: 150, ellipsis: true, scopedSlots: { customRender: "runTime" } }, { title: "已运行时间", dataIndex: "runTime", key: "runTime", width: 150, ellipsis: true, scopedSlots: { customRender: "runTime" } },
], ],
@ -285,21 +290,22 @@ export default {
}, },
// //
handleAdd() { handleAdd() {
this.temp = {
type: "add",
cycle: 0,
protocol: "http",
openStatus: 1,
timeOut: 0,
loginName: "jpomAgent",
};
this.editNodeVisible = true;
this.$nextTick(() => { this.$nextTick(() => {
setTimeout(() => { setTimeout(() => {
this.introGuide(); this.introGuide();
}, 500); }, 500);
this.$refs["editNodeForm"].resetFields(); this.$refs["editNodeForm"] && this.$refs["editNodeForm"].resetFields();
this.temp = {
type: "add",
cycle: 0,
protocol: "http",
openStatus: 1,
timeOut: 0,
loginName: "jpomAgent",
};
this.editNodeVisible = true;
}); });
this.loadSshList();
}, },
// //
handleTerminal(record) { handleTerminal(record) {
@ -358,7 +364,7 @@ export default {
// //
handleNode(record) { handleNode(record) {
this.temp = Object.assign(record); this.temp = Object.assign(record);
this.drawerTitle = `${this.temp.id} (${this.temp.url})`; this.drawerTitle = `${this.temp.name} (${this.temp.url})`;
this.drawerVisible = true; this.drawerVisible = true;
let nodeId = this.$route.query.nodeId; let nodeId = this.$route.query.nodeId;
if (nodeId !== record.id) { if (nodeId !== record.id) {
@ -367,6 +373,37 @@ export default {
}); });
} }
}, },
syncNode(node) {
syncProject(node.nodeId).then((res) => {
if (res.code == 200) {
this.$notification.success({
message: res.msg,
duration: 4,
});
return false;
}
});
},
delNodeCache(node) {
this.$confirm({
title: "系统提示",
content: "确定要清除该节点下面的项目缓存信息吗?",
okText: "确认",
cancelText: "取消",
onOk: () => {
//
delProjectCache(node.nodeId).then((res) => {
if (res.code == 200) {
this.$notification.success({
message: res.msg,
duration: 4,
});
return false;
}
});
},
});
},
// //
onClose() { onClose() {
this.drawerVisible = false; this.drawerVisible = false;

View File

@ -173,11 +173,11 @@ export default {
</script> </script>
<style scoped> <style scoped>
.sider { .sider {
height: calc(100vh - 75px); /* height: calc(100vh - 75px); */
overflow-y: auto; overflow-y: auto;
} }
.layout-content { .layout-content {
height: calc(100vh - 95px); /* min-height: calc(100vh); */
overflow-y: auto; overflow-y: auto;
} }
/* .node-layout { /* .node-layout {

View File

@ -12,6 +12,9 @@
<a-button type="primary" @click="batchRestart">批量重启</a-button> <a-button type="primary" @click="batchRestart">批量重启</a-button>
<a-button type="danger" @click="batchStop">批量关闭</a-button> <a-button type="danger" @click="batchStop">批量关闭</a-button>
状态数据是异步获取有一定时间延迟 状态数据是异步获取有一定时间延迟
<a-tooltip placement="topLeft" title="清除服务端缓存节点所有的项目信息, 需要重新同步">
<a-icon @click="delAll()" type="delete" />
</a-tooltip>
</div> </div>
<a-table <a-table
:data-source="projList" :data-source="projList"
@ -50,7 +53,7 @@
</div> </div>
</template> </template>
<script> <script>
import { getProjectList } from "@/api/node"; import { getProjectList, delAllProjectCache } from "@/api/node";
import { restartProject, startProject, stopProject, getRuningProjectInfo, runModeList } from "@/api/node-project"; import { restartProject, startProject, stopProject, getRuningProjectInfo, runModeList } from "@/api/node-project";
import { mapGetters } from "vuex"; import { mapGetters } from "vuex";
import File from "../node/node-layout/project/project-file"; import File from "../node/node-layout/project/project-file";
@ -272,6 +275,26 @@ export default {
} }
this.getNodeProjectData(); this.getNodeProjectData();
}, },
delAll() {
this.$confirm({
title: "系统提示",
content: "确定要清除服务端所有的项目缓存信息吗?",
okText: "确认",
cancelText: "取消",
onOk: () => {
//
delAllProjectCache().then((res) => {
if (res.code == 200) {
this.$notification.success({
message: res.msg,
duration: 4,
});
return false;
}
});
},
});
},
}, },
}; };
</script> </script>

View File

@ -196,7 +196,7 @@ export default {
// //
pagination() { pagination() {
return { return {
total: this.total, total: this.listQuery.total,
current: this.listQuery.page || 1, current: this.listQuery.page || 1,
pageSize: this.listQuery.limit || PAGE_DEFAULT_LIMIT, pageSize: this.listQuery.limit || PAGE_DEFAULT_LIMIT,
pageSizeOptions: PAGE_DEFAULT_SIZW_OPTIONS, pageSizeOptions: PAGE_DEFAULT_SIZW_OPTIONS,
@ -219,7 +219,7 @@ export default {
getRepositoryList(this.listQuery).then((res) => { getRepositoryList(this.listQuery).then((res) => {
if (res.code === 200) { if (res.code === 200) {
this.list = res.data.result; this.list = res.data.result;
this.total = res.data.total; this.listQuery.total = res.data.total;
} }
this.loading = false; this.loading = false;
}); });

View File

@ -188,7 +188,7 @@ export default {
getBackupList(this.listQuery).then((res) => { getBackupList(this.listQuery).then((res) => {
if (res.code === 200) { if (res.code === 200) {
this.list = res.data.result; this.list = res.data.result;
this.total = res.data.total; this.listQuery.total = res.data.total;
} }
this.loading = false; this.loading = false;
}); });