feat: [auth] The web interface adds ApiPermission for authentication.

This commit is contained in:
tjq 2024-02-13 11:21:14 +08:00
parent 05c22a5dc5
commit 31a7690844
18 changed files with 170 additions and 55 deletions

View File

@ -1,7 +1,6 @@
package tech.powerjob.server.core.uid; package tech.powerjob.server.core.uid;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import tech.powerjob.server.remote.server.self.ServerInfoService; import tech.powerjob.server.remote.server.self.ServerInfoService;
@ -22,7 +21,7 @@ public class IdGenerateService {
private static final int DATA_CENTER_ID = 0; private static final int DATA_CENTER_ID = 0;
public IdGenerateService(ServerInfoService serverInfoService) { public IdGenerateService(ServerInfoService serverInfoService) {
long id = serverInfoService.fetchServiceInfo().getId(); long id = serverInfoService.fetchCurrentServerInfo().getId();
snowFlakeIdGenerator = new SnowFlakeIdGenerator(DATA_CENTER_ID, id); snowFlakeIdGenerator = new SnowFlakeIdGenerator(DATA_CENTER_ID, id);
log.info("[IdGenerateService] initialize IdGenerateService successfully, ID:{}", id); log.info("[IdGenerateService] initialize IdGenerateService successfully, ID:{}", id);
} }

View File

@ -14,6 +14,13 @@ public interface ServerInfoService {
* fetch current server info * fetch current server info
* @return ServerInfo * @return ServerInfo
*/ */
ServerInfo fetchServiceInfo(); ServerInfo fetchCurrentServerInfo();
/**
* fetch schedule server info
* @param appId appId
* @return ServerInfo
*/
ServerInfo fetchAppServerInfo(Long appId);
} }

View File

@ -15,6 +15,7 @@ import tech.powerjob.server.common.module.ServerInfo;
import tech.powerjob.server.extension.LockService; import tech.powerjob.server.extension.LockService;
import tech.powerjob.server.persistence.remote.model.ServerInfoDO; import tech.powerjob.server.persistence.remote.model.ServerInfoDO;
import tech.powerjob.server.persistence.remote.repository.ServerInfoRepository; import tech.powerjob.server.persistence.remote.repository.ServerInfoRepository;
import tech.powerjob.server.remote.server.redirector.DesignateServer;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
@ -138,7 +139,13 @@ public class ServerInfoServiceImpl implements ServerInfoService {
} }
@Override @Override
public ServerInfo fetchServiceInfo() { public ServerInfo fetchCurrentServerInfo() {
return serverInfo;
}
@Override
@DesignateServer
public ServerInfo fetchAppServerInfo(Long appId) {
return serverInfo; return serverInfo;
} }
} }

View File

@ -61,7 +61,7 @@ public class WebAuthServiceImpl implements WebAuthService {
private void diffGrant(RoleScope roleScope, Long target, Role role, List<Long> uids, Map<Role, List<Long>> originRole2Uids) { private void diffGrant(RoleScope roleScope, Long target, Role role, List<Long> uids, Map<Role, List<Long>> originRole2Uids) {
Set<Long> orignUids = Sets.newHashSet(Optional.ofNullable(originRole2Uids.get(role)).orElse(Collections.emptyList())); Set<Long> originUids = Sets.newHashSet(Optional.ofNullable(originRole2Uids.get(role)).orElse(Collections.emptyList()));
Set<Long> currentUids = Sets.newHashSet(Optional.ofNullable(uids).orElse(Collections.emptyList())); Set<Long> currentUids = Sets.newHashSet(Optional.ofNullable(uids).orElse(Collections.emptyList()));
Map<String, Object> extraInfo = Maps.newHashMap(); Map<String, Object> extraInfo = Maps.newHashMap();
@ -69,12 +69,12 @@ public class WebAuthServiceImpl implements WebAuthService {
extraInfo.put("source", "diffGrant"); extraInfo.put("source", "diffGrant");
String extra = JsonUtils.toJSONString(extraInfo); String extra = JsonUtils.toJSONString(extraInfo);
Set<Long> allIds = Sets.newHashSet(orignUids); Set<Long> allIds = Sets.newHashSet(originUids);
allIds.addAll(currentUids); allIds.addAll(currentUids);
Set<Long> allIds2 = Sets.newHashSet(allIds); Set<Long> allIds2 = Sets.newHashSet(allIds);
// orignUids 不在 currentUids需要取消授权 // originUids 不在 currentUids需要取消授权
allIds.removeAll(currentUids); allIds.removeAll(currentUids);
allIds.forEach(cancelPermissionUid -> { allIds.forEach(cancelPermissionUid -> {
powerJobPermissionService.retrievePermission(roleScope, target, cancelPermissionUid, role); powerJobPermissionService.retrievePermission(roleScope, target, cancelPermissionUid, role);
@ -82,7 +82,7 @@ public class WebAuthServiceImpl implements WebAuthService {
}); });
// currentUids 当不在 orignUids需要增加授权 // currentUids 当不在 orignUids需要增加授权
allIds2.removeAll(orignUids); allIds2.removeAll(originUids);
allIds2.forEach(addPermissionUid -> { allIds2.forEach(addPermissionUid -> {
powerJobPermissionService.grantPermission(roleScope, target, addPermissionUid, role, extra); powerJobPermissionService.grantPermission(roleScope, target, addPermissionUid, role, extra);
log.info("[WebAuthService] [diffGrant] grantPermission: roleScope={},target={},uid={},role={},extra={}", roleScope, target, addPermissionUid, role, extra); log.info("[WebAuthService] [diffGrant] grantPermission: roleScope={},target={},uid={},role={},extra={}", roleScope, target, addPermissionUid, role, extra);

View File

@ -41,7 +41,7 @@ public class SwaggerConfig {
return new OpenAPI() return new OpenAPI()
.info(new Info().title("PowerJob") .info(new Info().title("PowerJob")
.description("Distributed scheduling and computing framework.") .description("Distributed scheduling and computing framework.")
.version(serverInfoService.fetchServiceInfo().getVersion()) .version(serverInfoService.fetchCurrentServerInfo().getVersion())
.contact(contact) .contact(contact)
.license(new License().name("Apache License 2.0").url("https://github.com/PowerJob/PowerJob/blob/master/LICENSE"))); .license(new License().name("Apache License 2.0").url("https://github.com/PowerJob/PowerJob/blob/master/LICENSE")));
} }

View File

@ -19,7 +19,7 @@ import java.util.List;
public class ServerInfoAwareProcessor { public class ServerInfoAwareProcessor {
public ServerInfoAwareProcessor(ServerInfoService serverInfoService, List<ServerInfoAware> awareList) { public ServerInfoAwareProcessor(ServerInfoService serverInfoService, List<ServerInfoAware> awareList) {
final ServerInfo serverInfo = serverInfoService.fetchServiceInfo(); final ServerInfo serverInfo = serverInfoService.fetchCurrentServerInfo();
log.info("[ServerInfoAwareProcessor] current server info: {}", serverInfo); log.info("[ServerInfoAwareProcessor] current server info: {}", serverInfo);
awareList.forEach(aware -> { awareList.forEach(aware -> {
aware.setServerInfo(serverInfo); aware.setServerInfo(serverInfo);

View File

@ -20,12 +20,10 @@ import tech.powerjob.server.auth.interceptor.ApiPermission;
import tech.powerjob.server.auth.plugin.ModifyOrCreateDynamicPermission; import tech.powerjob.server.auth.plugin.ModifyOrCreateDynamicPermission;
import tech.powerjob.server.auth.plugin.SaveAppGrantPermissionPlugin; import tech.powerjob.server.auth.plugin.SaveAppGrantPermissionPlugin;
import tech.powerjob.server.auth.service.WebAuthService; import tech.powerjob.server.auth.service.WebAuthService;
import tech.powerjob.server.core.service.AppInfoService;
import tech.powerjob.server.persistence.PageResult; import tech.powerjob.server.persistence.PageResult;
import tech.powerjob.server.persistence.QueryConvertUtils; import tech.powerjob.server.persistence.QueryConvertUtils;
import tech.powerjob.server.persistence.remote.model.AppInfoDO; import tech.powerjob.server.persistence.remote.model.AppInfoDO;
import tech.powerjob.server.persistence.remote.repository.AppInfoRepository; import tech.powerjob.server.persistence.remote.repository.AppInfoRepository;
import tech.powerjob.server.web.request.AppAssertRequest;
import tech.powerjob.server.web.request.ComponentUserRoleInfo; import tech.powerjob.server.web.request.ComponentUserRoleInfo;
import tech.powerjob.server.web.request.ModifyAppInfoRequest; import tech.powerjob.server.web.request.ModifyAppInfoRequest;
import tech.powerjob.server.web.request.QueryAppInfoRequest; import tech.powerjob.server.web.request.QueryAppInfoRequest;
@ -50,12 +48,8 @@ public class AppInfoController {
private final WebAuthService webAuthService; private final WebAuthService webAuthService;
private final AppInfoService appInfoService;
private final AppInfoRepository appInfoRepository; private final AppInfoRepository appInfoRepository;
private static final int MAX_APP_NUM = 200;
@PostMapping("/save") @PostMapping("/save")
@ApiPermission(name = "App-Save", roleScope = RoleScope.APP, dynamicPermissionPlugin = ModifyOrCreateDynamicPermission.class, grandPermissionPlugin = SaveAppGrantPermissionPlugin.class) @ApiPermission(name = "App-Save", roleScope = RoleScope.APP, dynamicPermissionPlugin = ModifyOrCreateDynamicPermission.class, grandPermissionPlugin = SaveAppGrantPermissionPlugin.class)
public ResultDTO<AppInfoVO> saveAppInfo(@RequestBody ModifyAppInfoRequest req) { public ResultDTO<AppInfoVO> saveAppInfo(@RequestBody ModifyAppInfoRequest req) {
@ -102,11 +96,6 @@ public class AppInfoController {
return ResultDTO.success(null); return ResultDTO.success(null);
} }
@PostMapping("/assert")
public ResultDTO<Long> assertApp(@RequestBody AppAssertRequest request) {
return ResultDTO.success(appInfoService.assertApp(request.getAppName(), request.getPassword()));
}
@PostMapping("/list") @PostMapping("/list")
@ApiPermission(name = "Namespace-List", roleScope = RoleScope.APP, requiredPermission = Permission.NONE) @ApiPermission(name = "Namespace-List", roleScope = RoleScope.APP, requiredPermission = Permission.NONE)
public ResultDTO<PageResult<AppInfoVO>> listAppInfoByQuery(@RequestBody QueryAppInfoRequest queryAppInfoRequest) { public ResultDTO<PageResult<AppInfoVO>> listAppInfoByQuery(@RequestBody QueryAppInfoRequest queryAppInfoRequest) {
@ -146,7 +135,6 @@ public class AppInfoController {
return ResultDTO.success(pageRet); return ResultDTO.success(pageRet);
} }
private List<AppInfoVO> convert(List<AppInfoDO> data, boolean fillDetail) { private List<AppInfoVO> convert(List<AppInfoDO> data, boolean fillDetail) {
if (CollectionUtils.isEmpty(data)) { if (CollectionUtils.isEmpty(data)) {
return Lists.newLinkedList(); return Lists.newLinkedList();

View File

@ -4,11 +4,13 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils; import org.apache.commons.lang3.time.DateFormatUtils;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import tech.powerjob.common.OmsConstant; import tech.powerjob.common.OmsConstant;
import tech.powerjob.common.response.ResultDTO; import tech.powerjob.common.response.ResultDTO;
import tech.powerjob.server.auth.Permission;
import tech.powerjob.server.auth.RoleScope;
import tech.powerjob.server.auth.interceptor.ApiPermission;
import tech.powerjob.server.common.constants.ContainerSourceType; import tech.powerjob.server.common.constants.ContainerSourceType;
import tech.powerjob.server.common.constants.SwitchableStatus; import tech.powerjob.server.common.constants.SwitchableStatus;
import tech.powerjob.server.common.utils.OmsFileUtils; import tech.powerjob.server.common.utils.OmsFileUtils;
@ -39,22 +41,25 @@ import java.util.stream.Collectors;
@RequestMapping("/container") @RequestMapping("/container")
public class ContainerController { public class ContainerController {
private final int port;
private final ContainerService containerService; private final ContainerService containerService;
private final AppInfoRepository appInfoRepository; private final AppInfoRepository appInfoRepository;
private final ContainerInfoRepository containerInfoRepository; private final ContainerInfoRepository containerInfoRepository;
public ContainerController(@Value("${server.port}") int port, ContainerService containerService, AppInfoRepository appInfoRepository, ContainerInfoRepository containerInfoRepository) { public ContainerController(ContainerService containerService, AppInfoRepository appInfoRepository, ContainerInfoRepository containerInfoRepository) {
this.port = port;
this.containerService = containerService; this.containerService = containerService;
this.appInfoRepository = appInfoRepository; this.appInfoRepository = appInfoRepository;
this.containerInfoRepository = containerInfoRepository; this.containerInfoRepository = containerInfoRepository;
} }
/**
* 暴露给 worker 的下载端口制品本身 version 不可枚举不单独鉴权
* 如果对此有安全性需求可自行实现加密鉴权逻辑或者干脆走自己的下载通道下载制品
* @param version 容器版本
* @param response 响应
* @throws IOException 异常
*/
@GetMapping("/downloadJar") @GetMapping("/downloadJar")
public void downloadJar(String version, HttpServletResponse response) throws IOException { public void downloadJar(String version, HttpServletResponse response) throws IOException {
File file = containerService.fetchContainerJarFile(version); File file = containerService.fetchContainerJarFile(version);
@ -66,12 +71,14 @@ public class ContainerController {
} }
@PostMapping("/downloadContainerTemplate") @PostMapping("/downloadContainerTemplate")
@ApiPermission(name = "Container-DownloadContainerTemplate", roleScope = RoleScope.APP, requiredPermission = Permission.READ)
public void downloadContainerTemplate(@RequestBody GenerateContainerTemplateRequest req, HttpServletResponse response) throws IOException { public void downloadContainerTemplate(@RequestBody GenerateContainerTemplateRequest req, HttpServletResponse response) throws IOException {
File zipFile = ContainerTemplateGenerator.generate(req.getGroup(), req.getArtifact(), req.getName(), req.getPackageName(), req.getJavaVersion()); File zipFile = ContainerTemplateGenerator.generate(req.getGroup(), req.getArtifact(), req.getName(), req.getPackageName(), req.getJavaVersion());
OmsFileUtils.file2HttpResponse(zipFile, response); OmsFileUtils.file2HttpResponse(zipFile, response);
} }
@PostMapping("/jarUpload") @PostMapping("/jarUpload")
@ApiPermission(name = "Container-JarUpload", roleScope = RoleScope.APP, requiredPermission = Permission.OPS)
public ResultDTO<String> fileUpload(@RequestParam("file") MultipartFile file) throws Exception { public ResultDTO<String> fileUpload(@RequestParam("file") MultipartFile file) throws Exception {
if (file == null || file.isEmpty()) { if (file == null || file.isEmpty()) {
return ResultDTO.failed("empty file"); return ResultDTO.failed("empty file");
@ -80,6 +87,7 @@ public class ContainerController {
} }
@PostMapping("/save") @PostMapping("/save")
@ApiPermission(name = "Container-Save", roleScope = RoleScope.APP, requiredPermission = Permission.OPS)
public ResultDTO<Void> saveContainer(@RequestBody SaveContainerInfoRequest request) { public ResultDTO<Void> saveContainer(@RequestBody SaveContainerInfoRequest request) {
request.valid(); request.valid();
@ -93,12 +101,14 @@ public class ContainerController {
} }
@GetMapping("/delete") @GetMapping("/delete")
@ApiPermission(name = "Container-Delete", roleScope = RoleScope.APP, requiredPermission = Permission.OPS)
public ResultDTO<Void> deleteContainer(Long appId, Long containerId) { public ResultDTO<Void> deleteContainer(Long appId, Long containerId) {
containerService.delete(appId, containerId); containerService.delete(appId, containerId);
return ResultDTO.success(null); return ResultDTO.success(null);
} }
@GetMapping("/list") @GetMapping("/list")
@ApiPermission(name = "Container-List", roleScope = RoleScope.APP, requiredPermission = Permission.READ)
public ResultDTO<List<ContainerInfoVO>> listContainers(Long appId) { public ResultDTO<List<ContainerInfoVO>> listContainers(Long appId) {
List<ContainerInfoVO> res = containerInfoRepository.findByAppIdAndStatusNot(appId, SwitchableStatus.DELETED.getV()) List<ContainerInfoVO> res = containerInfoRepository.findByAppIdAndStatusNot(appId, SwitchableStatus.DELETED.getV())
.stream().map(ContainerController::convert).collect(Collectors.toList()); .stream().map(ContainerController::convert).collect(Collectors.toList());
@ -106,6 +116,7 @@ public class ContainerController {
} }
@GetMapping("/listDeployedWorker") @GetMapping("/listDeployedWorker")
@ApiPermission(name = "Container-ListDeployedWorker", roleScope = RoleScope.APP, requiredPermission = Permission.READ)
public ResultDTO<String> listDeployedWorker(Long appId, Long containerId, HttpServletResponse response) { public ResultDTO<String> listDeployedWorker(Long appId, Long containerId, HttpServletResponse response) {
AppInfoDO appInfoDO = appInfoRepository.findById(appId).orElseThrow(() -> new IllegalArgumentException("can't find app by id:" + appId)); AppInfoDO appInfoDO = appInfoRepository.findById(appId).orElseThrow(() -> new IllegalArgumentException("can't find app by id:" + appId));
String targetServer = appInfoDO.getCurrentServer(); String targetServer = appInfoDO.getCurrentServer();

View File

@ -2,6 +2,9 @@ package tech.powerjob.server.web.controller;
import tech.powerjob.common.enums.InstanceStatus; import tech.powerjob.common.enums.InstanceStatus;
import tech.powerjob.common.response.ResultDTO; import tech.powerjob.common.response.ResultDTO;
import tech.powerjob.server.auth.Permission;
import tech.powerjob.server.auth.RoleScope;
import tech.powerjob.server.auth.interceptor.ApiPermission;
import tech.powerjob.server.common.utils.OmsFileUtils; import tech.powerjob.server.common.utils.OmsFileUtils;
import tech.powerjob.server.persistence.PageResult; import tech.powerjob.server.persistence.PageResult;
import tech.powerjob.server.persistence.StringPage; import tech.powerjob.server.persistence.StringPage;
@ -43,8 +46,6 @@ import java.util.stream.Collectors;
@RequestMapping("/instance") @RequestMapping("/instance")
public class InstanceController { public class InstanceController {
@Resource @Resource
private InstanceService instanceService; private InstanceService instanceService;
@Resource @Resource
@ -56,18 +57,21 @@ public class InstanceController {
private InstanceInfoRepository instanceInfoRepository; private InstanceInfoRepository instanceInfoRepository;
@GetMapping("/stop") @GetMapping("/stop")
@ApiPermission(name = "Instance-Stop", roleScope = RoleScope.APP, requiredPermission = Permission.OPS)
public ResultDTO<Void> stopInstance(Long appId,Long instanceId) { public ResultDTO<Void> stopInstance(Long appId,Long instanceId) {
instanceService.stopInstance(appId,instanceId); instanceService.stopInstance(appId,instanceId);
return ResultDTO.success(null); return ResultDTO.success(null);
} }
@GetMapping("/retry") @GetMapping("/retry")
@ApiPermission(name = "Instance-Retry", roleScope = RoleScope.APP, requiredPermission = Permission.OPS)
public ResultDTO<Void> retryInstance(String appId, Long instanceId) { public ResultDTO<Void> retryInstance(String appId, Long instanceId) {
instanceService.retryInstance(Long.valueOf(appId), instanceId); instanceService.retryInstance(Long.valueOf(appId), instanceId);
return ResultDTO.success(null); return ResultDTO.success(null);
} }
@GetMapping("/detail") @GetMapping("/detail")
@ApiPermission(name = "Instance-Detail", roleScope = RoleScope.APP, requiredPermission = Permission.READ)
public ResultDTO<InstanceDetailVO> getInstanceDetail(Long appId, Long instanceId) { public ResultDTO<InstanceDetailVO> getInstanceDetail(Long appId, Long instanceId) {
// 兼容老版本前端不存在 appId 的场景 // 兼容老版本前端不存在 appId 的场景
@ -79,11 +83,13 @@ public class InstanceController {
} }
@GetMapping("/log") @GetMapping("/log")
@ApiPermission(name = "Instance-Log", roleScope = RoleScope.APP, requiredPermission = Permission.OPS)
public ResultDTO<StringPage> getInstanceLog(Long appId, Long instanceId, Long index) { public ResultDTO<StringPage> getInstanceLog(Long appId, Long instanceId, Long index) {
return ResultDTO.success(instanceLogService.fetchInstanceLog(appId, instanceId, index)); return ResultDTO.success(instanceLogService.fetchInstanceLog(appId, instanceId, index));
} }
@GetMapping("/downloadLogUrl") @GetMapping("/downloadLogUrl")
@ApiPermission(name = "Instance-FetchDownloadLogUrl", roleScope = RoleScope.APP, requiredPermission = Permission.READ)
public ResultDTO<String> getDownloadUrl(Long appId, Long instanceId) { public ResultDTO<String> getDownloadUrl(Long appId, Long instanceId) {
return ResultDTO.success(instanceLogService.fetchDownloadUrl(appId, instanceId)); return ResultDTO.success(instanceLogService.fetchDownloadUrl(appId, instanceId));
} }
@ -115,6 +121,7 @@ public class InstanceController {
} }
@PostMapping("/list") @PostMapping("/list")
@ApiPermission(name = "Instance-List", roleScope = RoleScope.APP, requiredPermission = Permission.READ)
public ResultDTO<PageResult<InstanceInfoVO>> list(@RequestBody QueryInstanceRequest request) { public ResultDTO<PageResult<InstanceInfoVO>> list(@RequestBody QueryInstanceRequest request) {
Sort sort = Sort.by(Sort.Direction.DESC, "gmtModified"); Sort sort = Sort.by(Sort.Direction.DESC, "gmtModified");

View File

@ -3,6 +3,9 @@ package tech.powerjob.server.web.controller;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import tech.powerjob.common.request.http.SaveJobInfoRequest; import tech.powerjob.common.request.http.SaveJobInfoRequest;
import tech.powerjob.common.response.ResultDTO; import tech.powerjob.common.response.ResultDTO;
import tech.powerjob.server.auth.Permission;
import tech.powerjob.server.auth.RoleScope;
import tech.powerjob.server.auth.interceptor.ApiPermission;
import tech.powerjob.server.common.constants.SwitchableStatus; import tech.powerjob.server.common.constants.SwitchableStatus;
import tech.powerjob.server.persistence.PageResult; import tech.powerjob.server.persistence.PageResult;
import tech.powerjob.server.persistence.remote.model.JobInfoDO; import tech.powerjob.server.persistence.remote.model.JobInfoDO;
@ -39,39 +42,46 @@ public class JobController {
private JobInfoRepository jobInfoRepository; private JobInfoRepository jobInfoRepository;
@PostMapping("/save") @PostMapping("/save")
@ApiPermission(name = "Job-Save", roleScope = RoleScope.APP, requiredPermission = Permission.WRITE)
public ResultDTO<Void> saveJobInfo(@RequestBody SaveJobInfoRequest request) { public ResultDTO<Void> saveJobInfo(@RequestBody SaveJobInfoRequest request) {
jobService.saveJob(request); jobService.saveJob(request);
return ResultDTO.success(null); return ResultDTO.success(null);
} }
@PostMapping("/copy") @PostMapping("/copy")
@ApiPermission(name = "Job-Copy", roleScope = RoleScope.APP, requiredPermission = Permission.WRITE)
public ResultDTO<JobInfoVO> copyJob(String jobId) { public ResultDTO<JobInfoVO> copyJob(String jobId) {
return ResultDTO.success(JobInfoVO.from(jobService.copyJob(Long.valueOf(jobId)))); return ResultDTO.success(JobInfoVO.from(jobService.copyJob(Long.valueOf(jobId))));
} }
@GetMapping("/export") @GetMapping("/export")
@ApiPermission(name = "Job-Export", roleScope = RoleScope.APP, requiredPermission = Permission.READ)
public ResultDTO<SaveJobInfoRequest> exportJob(String jobId) { public ResultDTO<SaveJobInfoRequest> exportJob(String jobId) {
return ResultDTO.success(jobService.exportJob(Long.valueOf(jobId))); return ResultDTO.success(jobService.exportJob(Long.valueOf(jobId)));
} }
@GetMapping("/disable") @GetMapping("/disable")
@ApiPermission(name = "Job-Disable", roleScope = RoleScope.APP, requiredPermission = Permission.WRITE)
public ResultDTO<Void> disableJob(String jobId) { public ResultDTO<Void> disableJob(String jobId) {
jobService.disableJob(Long.valueOf(jobId)); jobService.disableJob(Long.valueOf(jobId));
return ResultDTO.success(null); return ResultDTO.success(null);
} }
@GetMapping("/delete") @GetMapping("/delete")
@ApiPermission(name = "Job-Delete", roleScope = RoleScope.APP, requiredPermission = Permission.WRITE)
public ResultDTO<Void> deleteJob(String jobId) { public ResultDTO<Void> deleteJob(String jobId) {
jobService.deleteJob(Long.valueOf(jobId)); jobService.deleteJob(Long.valueOf(jobId));
return ResultDTO.success(null); return ResultDTO.success(null);
} }
@GetMapping("/run") @GetMapping("/run")
@ApiPermission(name = "Job-Copy", roleScope = RoleScope.APP, requiredPermission = Permission.OPS)
public ResultDTO<Long> runImmediately(String appId, String jobId, @RequestParam(required = false) String instanceParams) { public ResultDTO<Long> runImmediately(String appId, String jobId, @RequestParam(required = false) String instanceParams) {
return ResultDTO.success(jobService.runJob(Long.valueOf(appId), Long.valueOf(jobId), instanceParams, 0L)); return ResultDTO.success(jobService.runJob(Long.valueOf(appId), Long.valueOf(jobId), instanceParams, 0L));
} }
@PostMapping("/list") @PostMapping("/list")
@ApiPermission(name = "Job-Copy", roleScope = RoleScope.APP, requiredPermission = Permission.READ)
public ResultDTO<PageResult<JobInfoVO>> listJobs(@RequestBody QueryJobInfoRequest request) { public ResultDTO<PageResult<JobInfoVO>> listJobs(@RequestBody QueryJobInfoRequest request) {
Sort sort = Sort.by(Sort.Direction.ASC, "id"); Sort sort = Sort.by(Sort.Direction.ASC, "id");

View File

@ -26,6 +26,7 @@ import tech.powerjob.server.web.converter.NamespaceConverter;
import tech.powerjob.server.web.request.ComponentUserRoleInfo; import tech.powerjob.server.web.request.ComponentUserRoleInfo;
import tech.powerjob.server.web.request.ModifyNamespaceRequest; import tech.powerjob.server.web.request.ModifyNamespaceRequest;
import tech.powerjob.server.web.request.QueryNamespaceRequest; import tech.powerjob.server.web.request.QueryNamespaceRequest;
import tech.powerjob.server.web.response.NamespaceBaseVO;
import tech.powerjob.server.web.response.NamespaceVO; import tech.powerjob.server.web.response.NamespaceVO;
import javax.annotation.Resource; import javax.annotation.Resource;
@ -147,6 +148,22 @@ public class NamespaceController {
return ResultDTO.success(ret); return ResultDTO.success(ret);
} }
@PostMapping("/listAll")
@ApiPermission(name = "Namespace-ListAll", roleScope = RoleScope.NAMESPACE, requiredPermission = Permission.NONE)
public ResultDTO<List<NamespaceBaseVO>> listAll() {
// 数量应该不是很多先简单处理不查询精简对象
List<NamespaceDO> namespaceRepositoryAll = namespaceRepository.findAll();
List<NamespaceBaseVO> namespaceBaseVOList = namespaceRepositoryAll.stream().map(nd -> {
NamespaceBaseVO nv = new NamespaceBaseVO();
nv.setId(nd.getId());
nv.setCode(nd.getCode());
nv.setName(nd.getName());
nv.genFrontName();
return nv;
}).collect(Collectors.toList());
return ResultDTO.success(namespaceBaseVOList);
}
private void fillPermissionInfo(NamespaceDO namespaceDO, NamespaceVO namespaceVO) { private void fillPermissionInfo(NamespaceDO namespaceDO, NamespaceVO namespaceVO) {
Long namespaceId = namespaceVO.getId(); Long namespaceId = namespaceVO.getId();

View File

@ -12,6 +12,8 @@ import tech.powerjob.common.enums.InstanceStatus;
import tech.powerjob.common.response.ResultDTO; import tech.powerjob.common.response.ResultDTO;
import tech.powerjob.server.common.constants.SwitchableStatus; import tech.powerjob.server.common.constants.SwitchableStatus;
import tech.powerjob.server.common.module.WorkerInfo; import tech.powerjob.server.common.module.WorkerInfo;
import tech.powerjob.server.persistence.remote.model.AppInfoDO;
import tech.powerjob.server.persistence.remote.repository.AppInfoRepository;
import tech.powerjob.server.persistence.remote.repository.InstanceInfoRepository; import tech.powerjob.server.persistence.remote.repository.InstanceInfoRepository;
import tech.powerjob.server.persistence.remote.repository.JobInfoRepository; import tech.powerjob.server.persistence.remote.repository.JobInfoRepository;
import tech.powerjob.server.remote.server.self.ServerInfoService; import tech.powerjob.server.remote.server.self.ServerInfoService;
@ -21,6 +23,7 @@ import tech.powerjob.server.web.response.WorkerStatusVO;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.TimeZone; import java.util.TimeZone;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -36,6 +39,8 @@ import java.util.stream.Collectors;
@RequiredArgsConstructor @RequiredArgsConstructor
public class SystemInfoController { public class SystemInfoController {
private final AppInfoRepository appInfoRepository;
private final JobInfoRepository jobInfoRepository; private final JobInfoRepository jobInfoRepository;
private final InstanceInfoRepository instanceInfoRepository; private final InstanceInfoRepository instanceInfoRepository;
@ -56,6 +61,14 @@ public class SystemInfoController {
SystemOverviewVO overview = new SystemOverviewVO(); SystemOverviewVO overview = new SystemOverviewVO();
Optional<AppInfoDO> appInfoOpt = appInfoRepository.findById(appId);
if (appInfoOpt.isPresent()) {
AppInfoDO appInfo = appInfoOpt.get();
overview.setAppId(appId);
overview.setAppName(appInfo.getAppName());
}
// 总任务数量 // 总任务数量
overview.setJobCount(jobInfoRepository.countByAppIdAndStatusNot(appId, SwitchableStatus.DELETED.getV())); overview.setJobCount(jobInfoRepository.countByAppIdAndStatusNot(appId, SwitchableStatus.DELETED.getV()));
// 运行任务数 // 运行任务数
@ -69,7 +82,8 @@ public class SystemInfoController {
// 服务器时间 // 服务器时间
overview.setServerTime(DateFormatUtils.format(new Date(), OmsConstant.TIME_PATTERN)); overview.setServerTime(DateFormatUtils.format(new Date(), OmsConstant.TIME_PATTERN));
overview.setServerInfo(serverInfoService.fetchServiceInfo()); overview.setWebServerInfo(serverInfoService.fetchCurrentServerInfo());
overview.setScheduleServerInfo(serverInfoService.fetchAppServerInfo(appId));
return ResultDTO.success(overview); return ResultDTO.success(overview);
} }

View File

@ -1,26 +1,28 @@
package tech.powerjob.server.web.controller; package tech.powerjob.server.web.controller;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.web.bind.annotation.*;
import tech.powerjob.common.request.http.SaveWorkflowNodeRequest; import tech.powerjob.common.request.http.SaveWorkflowNodeRequest;
import tech.powerjob.common.request.http.SaveWorkflowRequest; import tech.powerjob.common.request.http.SaveWorkflowRequest;
import tech.powerjob.common.response.ResultDTO; import tech.powerjob.common.response.ResultDTO;
import tech.powerjob.server.auth.Permission;
import tech.powerjob.server.auth.RoleScope;
import tech.powerjob.server.auth.interceptor.ApiPermission;
import tech.powerjob.server.common.constants.SwitchableStatus; import tech.powerjob.server.common.constants.SwitchableStatus;
import tech.powerjob.server.core.workflow.WorkflowService;
import tech.powerjob.server.persistence.PageResult; import tech.powerjob.server.persistence.PageResult;
import tech.powerjob.server.persistence.remote.model.WorkflowInfoDO; import tech.powerjob.server.persistence.remote.model.WorkflowInfoDO;
import tech.powerjob.server.persistence.remote.model.WorkflowNodeInfoDO; import tech.powerjob.server.persistence.remote.model.WorkflowNodeInfoDO;
import tech.powerjob.server.persistence.remote.repository.WorkflowInfoRepository; import tech.powerjob.server.persistence.remote.repository.WorkflowInfoRepository;
import tech.powerjob.server.core.workflow.WorkflowService;
import tech.powerjob.server.web.request.QueryWorkflowInfoRequest; import tech.powerjob.server.web.request.QueryWorkflowInfoRequest;
import tech.powerjob.server.web.response.WorkflowInfoVO; import tech.powerjob.server.web.response.WorkflowInfoVO;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.text.ParseException; import java.text.ParseException;
import java.util.List; import java.util.List;
import java.util.function.LongToDoubleFunction;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
@ -40,34 +42,40 @@ public class WorkflowController {
private WorkflowInfoRepository workflowInfoRepository; private WorkflowInfoRepository workflowInfoRepository;
@PostMapping("/save") @PostMapping("/save")
@ApiPermission(name = "Workflow-Save", roleScope = RoleScope.APP, requiredPermission = Permission.WRITE)
public ResultDTO<Long> save(@RequestBody SaveWorkflowRequest req) throws ParseException { public ResultDTO<Long> save(@RequestBody SaveWorkflowRequest req) throws ParseException {
return ResultDTO.success(workflowService.saveWorkflow(req)); return ResultDTO.success(workflowService.saveWorkflow(req));
} }
@PostMapping("/copy") @PostMapping("/copy")
@ApiPermission(name = "Workflow-Copy", roleScope = RoleScope.APP, requiredPermission = Permission.WRITE)
public ResultDTO<Long> copy(Long workflowId, Long appId) { public ResultDTO<Long> copy(Long workflowId, Long appId) {
return ResultDTO.success(workflowService.copyWorkflow(workflowId,appId)); return ResultDTO.success(workflowService.copyWorkflow(workflowId,appId));
} }
@GetMapping("/disable") @GetMapping("/disable")
@ApiPermission(name = "Workflow-Disable", roleScope = RoleScope.APP, requiredPermission = Permission.WRITE)
public ResultDTO<Void> disableWorkflow(Long workflowId, Long appId) { public ResultDTO<Void> disableWorkflow(Long workflowId, Long appId) {
workflowService.disableWorkflow(workflowId, appId); workflowService.disableWorkflow(workflowId, appId);
return ResultDTO.success(null); return ResultDTO.success(null);
} }
@GetMapping("/enable") @GetMapping("/enable")
@ApiPermission(name = "Workflow-Enable", roleScope = RoleScope.APP, requiredPermission = Permission.WRITE)
public ResultDTO<Void> enableWorkflow(Long workflowId, Long appId) { public ResultDTO<Void> enableWorkflow(Long workflowId, Long appId) {
workflowService.enableWorkflow(workflowId, appId); workflowService.enableWorkflow(workflowId, appId);
return ResultDTO.success(null); return ResultDTO.success(null);
} }
@GetMapping("/delete") @GetMapping("/delete")
@ApiPermission(name = "Workflow-Delete", roleScope = RoleScope.APP, requiredPermission = Permission.WRITE)
public ResultDTO<Void> deleteWorkflow(Long workflowId, Long appId) { public ResultDTO<Void> deleteWorkflow(Long workflowId, Long appId) {
workflowService.deleteWorkflow(workflowId, appId); workflowService.deleteWorkflow(workflowId, appId);
return ResultDTO.success(null); return ResultDTO.success(null);
} }
@PostMapping("/list") @PostMapping("/list")
@ApiPermission(name = "Workflow-List", roleScope = RoleScope.APP, requiredPermission = Permission.READ)
public ResultDTO<PageResult<WorkflowInfoVO>> list(@RequestBody QueryWorkflowInfoRequest req) { public ResultDTO<PageResult<WorkflowInfoVO>> list(@RequestBody QueryWorkflowInfoRequest req) {
Sort sort = Sort.by(Sort.Direction.DESC, "gmtCreate"); Sort sort = Sort.by(Sort.Direction.DESC, "gmtCreate");
@ -89,6 +97,7 @@ public class WorkflowController {
} }
@GetMapping("/run") @GetMapping("/run")
@ApiPermission(name = "Workflow-Run", roleScope = RoleScope.APP, requiredPermission = Permission.OPS)
public ResultDTO<Long> runWorkflow(Long workflowId, Long appId, public ResultDTO<Long> runWorkflow(Long workflowId, Long appId,
@RequestParam(required = false,defaultValue = "0") Long delay, @RequestParam(required = false,defaultValue = "0") Long delay,
@RequestParam(required = false) String initParams @RequestParam(required = false) String initParams
@ -97,12 +106,14 @@ public class WorkflowController {
} }
@GetMapping("/fetch") @GetMapping("/fetch")
@ApiPermission(name = "Workflow-Fetch", roleScope = RoleScope.APP, requiredPermission = Permission.READ)
public ResultDTO<WorkflowInfoVO> fetchWorkflow(Long workflowId, Long appId) { public ResultDTO<WorkflowInfoVO> fetchWorkflow(Long workflowId, Long appId) {
WorkflowInfoDO workflowInfoDO = workflowService.fetchWorkflow(workflowId, appId); WorkflowInfoDO workflowInfoDO = workflowService.fetchWorkflow(workflowId, appId);
return ResultDTO.success(WorkflowInfoVO.from(workflowInfoDO)); return ResultDTO.success(WorkflowInfoVO.from(workflowInfoDO));
} }
@PostMapping("/saveNode") @PostMapping("/saveNode")
@ApiPermission(name = "Workflow-SaveNode", roleScope = RoleScope.APP, requiredPermission = Permission.WRITE)
public ResultDTO<List<WorkflowNodeInfoDO>> addWorkflowNode(@RequestBody List<SaveWorkflowNodeRequest> request) { public ResultDTO<List<WorkflowNodeInfoDO>> addWorkflowNode(@RequestBody List<SaveWorkflowNodeRequest> request) {
return ResultDTO.success(workflowService.saveWorkflowNode(request)); return ResultDTO.success(workflowService.saveWorkflowNode(request));
} }

View File

@ -2,6 +2,9 @@ package tech.powerjob.server.web.controller;
import tech.powerjob.common.enums.WorkflowInstanceStatus; import tech.powerjob.common.enums.WorkflowInstanceStatus;
import tech.powerjob.common.response.ResultDTO; import tech.powerjob.common.response.ResultDTO;
import tech.powerjob.server.auth.Permission;
import tech.powerjob.server.auth.RoleScope;
import tech.powerjob.server.auth.interceptor.ApiPermission;
import tech.powerjob.server.persistence.PageResult; import tech.powerjob.server.persistence.PageResult;
import tech.powerjob.server.persistence.remote.model.WorkflowInstanceInfoDO; import tech.powerjob.server.persistence.remote.model.WorkflowInstanceInfoDO;
import tech.powerjob.server.persistence.remote.repository.WorkflowInstanceInfoRepository; import tech.powerjob.server.persistence.remote.repository.WorkflowInstanceInfoRepository;
@ -38,18 +41,21 @@ public class WorkflowInstanceController {
private WorkflowInstanceInfoRepository workflowInstanceInfoRepository; private WorkflowInstanceInfoRepository workflowInstanceInfoRepository;
@GetMapping("/stop") @GetMapping("/stop")
@ApiPermission(name = "WorkflowInstance-Stop", roleScope = RoleScope.APP, requiredPermission = Permission.OPS)
public ResultDTO<Void> stopWfInstance(Long wfInstanceId, Long appId) { public ResultDTO<Void> stopWfInstance(Long wfInstanceId, Long appId) {
workflowInstanceService.stopWorkflowInstanceEntrance(wfInstanceId, appId); workflowInstanceService.stopWorkflowInstanceEntrance(wfInstanceId, appId);
return ResultDTO.success(null); return ResultDTO.success(null);
} }
@RequestMapping("/retry") @RequestMapping("/retry")
@ApiPermission(name = "WorkflowInstance-Retry", roleScope = RoleScope.APP, requiredPermission = Permission.OPS)
public ResultDTO<Void> retryWfInstance(Long wfInstanceId, Long appId) { public ResultDTO<Void> retryWfInstance(Long wfInstanceId, Long appId) {
workflowInstanceService.retryWorkflowInstance(wfInstanceId, appId); workflowInstanceService.retryWorkflowInstance(wfInstanceId, appId);
return ResultDTO.success(null); return ResultDTO.success(null);
} }
@RequestMapping("/markNodeAsSuccess") @RequestMapping("/markNodeAsSuccess")
@ApiPermission(name = "WorkflowInstance-MarkNodeAsSuccess", roleScope = RoleScope.APP, requiredPermission = Permission.OPS)
public ResultDTO<Void> markNodeAsSuccess(Long wfInstanceId, Long appId, Long nodeId) { public ResultDTO<Void> markNodeAsSuccess(Long wfInstanceId, Long appId, Long nodeId) {
workflowInstanceService.markNodeAsSuccess(appId, wfInstanceId, nodeId); workflowInstanceService.markNodeAsSuccess(appId, wfInstanceId, nodeId);
return ResultDTO.success(null); return ResultDTO.success(null);
@ -57,12 +63,14 @@ public class WorkflowInstanceController {
@GetMapping("/info") @GetMapping("/info")
@ApiPermission(name = "WorkflowInstance-Info", roleScope = RoleScope.APP, requiredPermission = Permission.READ)
public ResultDTO<WorkflowInstanceInfoVO> getInfo(Long wfInstanceId, Long appId) { public ResultDTO<WorkflowInstanceInfoVO> getInfo(Long wfInstanceId, Long appId) {
WorkflowInstanceInfoDO wfInstanceDO = workflowInstanceService.fetchWfInstance(wfInstanceId, appId); WorkflowInstanceInfoDO wfInstanceDO = workflowInstanceService.fetchWfInstance(wfInstanceId, appId);
return ResultDTO.success(WorkflowInstanceInfoVO.from(wfInstanceDO, cacheService.getWorkflowName(wfInstanceDO.getWorkflowId()))); return ResultDTO.success(WorkflowInstanceInfoVO.from(wfInstanceDO, cacheService.getWorkflowName(wfInstanceDO.getWorkflowId())));
} }
@PostMapping("/list") @PostMapping("/list")
@ApiPermission(name = "WorkflowInstance-List", roleScope = RoleScope.APP, requiredPermission = Permission.READ)
public ResultDTO<PageResult<WorkflowInstanceInfoVO>> listWfInstance(@RequestBody QueryWorkflowInstanceRequest req) { public ResultDTO<PageResult<WorkflowInstanceInfoVO>> listWfInstance(@RequestBody QueryWorkflowInstanceRequest req) {
Sort sort = Sort.by(Sort.Direction.DESC, "gmtModified"); Sort sort = Sort.by(Sort.Direction.DESC, "gmtModified");
PageRequest pageable = PageRequest.of(req.getIndex(), req.getPageSize(), sort); PageRequest pageable = PageRequest.of(req.getIndex(), req.getPageSize(), sort);

View File

@ -19,6 +19,8 @@ public class AppInfoVO implements Serializable {
private String appName; private String appName;
private Long namespaceId;
/** /**
* 描述 * 描述
*/ */

View File

@ -0,0 +1,38 @@
package tech.powerjob.server.web.response;
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
/**
* namespace 基本 VO 对象用于列表渲染
*
* @author tjq
* @since 2024/2/12
*/
@Getter
@Setter
public class NamespaceBaseVO implements Serializable {
protected Long id;
/**
* 空间唯一标识
*/
protected String code;
/**
* 空间名称比如中文描述XX部门XX空间
*/
protected String name;
/**
* 前端名称拼接 code + name更容易辨认
*/
protected String frontName;
public void genFrontName() {
frontName = String.format("%s(%s)", name, code);
}
}

View File

@ -5,7 +5,6 @@ import lombok.Setter;
import lombok.ToString; import lombok.ToString;
import tech.powerjob.server.web.request.ComponentUserRoleInfo; import tech.powerjob.server.web.request.ComponentUserRoleInfo;
import java.io.Serializable;
import java.util.Date; import java.util.Date;
/** /**
@ -17,19 +16,7 @@ import java.util.Date;
@Getter @Getter
@Setter @Setter
@ToString @ToString
public class NamespaceVO implements Serializable { public class NamespaceVO extends NamespaceBaseVO {
private Long id;
/**
* 空间唯一标识
*/
private String code;
/**
* 空间名称比如中文描述XX部门XX空间
*/
private String name;
private String dept; private String dept;
private String tags; private String tags;

View File

@ -1,8 +1,6 @@
package tech.powerjob.server.web.response; package tech.powerjob.server.web.response;
import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.Getter;
import tech.powerjob.server.common.module.ServerInfo; import tech.powerjob.server.common.module.ServerInfo;
/** /**
@ -14,6 +12,10 @@ import tech.powerjob.server.common.module.ServerInfo;
@Data @Data
public class SystemOverviewVO { public class SystemOverviewVO {
private Long appId;
private String appName;
private long jobCount; private long jobCount;
private long runningInstanceCount; private long runningInstanceCount;
private long failedInstanceCount; private long failedInstanceCount;
@ -26,5 +28,12 @@ public class SystemOverviewVO {
*/ */
private String serverTime; private String serverTime;
private ServerInfo serverInfo; /**
* 处理当前 WEB 服务的 server 信息
*/
private ServerInfo webServerInfo;
/**
* 调度服务器信息
*/
private ServerInfo scheduleServerInfo;
} }