Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## [0.2] - 2025-08-12
- 提升多账号情况下资产采集的时效性
- 回流采集异常日志、支持手动云账号触发采集任务
- 前端交互和展示优化,提升使用体验
- 其他bug修复

## [0.1] - 2025-05-01
- 项目初始化,基础功能上线,奠定平台架构基础。
- 云账号管理:支持云账号的创建与查询,便于多云环境统一纳管,提升账号安全性与可追溯性。
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG_EN.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## [0.2] - 2025-08-12
- Improve the timeliness of asset collection in the case of multiple accounts
- Collection of abnormal logs and manual cloud account-triggered collection tasks
- Optimized frontend interaction and display for better user experience
- Other bug fixes

## [0.1] - 2025-05-01
- Project initialization, foundational features launched, establishing the platform architecture.
- Cloud Account Management: Supports creation and query of cloud accounts, enabling unified management in multi-cloud environments, enhancing account security and traceability.
Expand Down
24 changes: 22 additions & 2 deletions app/api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<artifactId>cloudrec</artifactId>
<groupId>com.alipay</groupId>
<version>0.2.0-SNAPSHOT</version>
<version>0.2.1-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<artifactId>api</artifactId>
Expand All @@ -18,7 +18,7 @@
<dependency>
<groupId>com.alipay</groupId>
<artifactId>application</artifactId>
<version>0.2.0-SNAPSHOT</version>
<version>0.2.1-SNAPSHOT</version>
</dependency>
<!-- Spring Boot Starter Test -->
<dependency>
Expand All @@ -27,4 +27,24 @@
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<!-- Maven Surefire Plugin for unified JUnit 5 testing -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M7</version>
<configuration>
<!-- Enable JUnit Platform for JUnit 5 support -->
<useJUnitPlatform>true</useJUnitPlatform>
<includes>
<include>**/*Test.java</include>
<include>**/*Tests.java</include>
</includes>
<testFailureIgnore>false</testFailureIgnore>
</configuration>
</plugin>
</plugins>
</build>
</project>
23 changes: 16 additions & 7 deletions app/api/src/main/java/com/alipay/api/collector/CollectorApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,7 @@
import com.alipay.application.service.collector.AgentService;
import com.alipay.application.service.collector.domain.TaskResp;
import com.alipay.application.service.resource.SaveResourceService;
import com.alipay.application.share.request.collector.AcceptSupportResourceTypeRequest;
import com.alipay.application.share.request.collector.LogRequest;
import com.alipay.application.share.request.collector.QueryCloudAccountRequest;
import com.alipay.application.share.request.collector.RunningFinishSignalRequest;
import com.alipay.application.share.request.collector.*;
import com.alipay.application.share.request.resource.DataPushRequest;
import com.alipay.application.share.vo.ApiResponse;
import com.alipay.application.share.vo.collector.AgentCloudAccountVO;
Expand Down Expand Up @@ -73,13 +70,25 @@ public ApiResponse<String> acceptResourceData(@Validated @RequestBody DataPushRe
}

@PostMapping("/acceptRunningFinishSignal")
public void acceptRunningFinishSignal(@Validated @RequestBody RunningFinishSignalRequest req,
BindingResult err) {
public ApiResponse<String> acceptRunningFinishSignal(@Validated @RequestBody RunningFinishSignalRequest req,
BindingResult err) {
if (err.hasErrors()) {
throw new IllegalArgumentException(err.toString());
}

agentService.runningFinishSignal(req.getCloudAccountId(), req.getTaskId());
return ApiResponse.SUCCESS;
}

@PostMapping("/acceptRunningStartSignal")
public ApiResponse<String> acceptRunningStartSignal(HttpServletRequest request, @Validated @RequestBody RunningStartSignalRequest req,
BindingResult err) {
if (err.hasErrors()) {
throw new IllegalArgumentException(err.toString());
}

agentService.runningStartSignal(request.getHeader(PERSISTENT_TOKEN), req.getCloudAccountId(), req.getCollectRecordInfo());
return ApiResponse.SUCCESS;
}


Expand All @@ -94,7 +103,7 @@ public void acceptRunningFinishSignal(@Validated @RequestBody RunningFinishSigna
public ApiResponse<List<AgentCloudAccountVO>> listCloudAccount(HttpServletRequest request,
@RequestBody QueryCloudAccountRequest req) {
return agentService.queryCloudAccountList(request.getHeader(PERSISTENT_TOKEN),
req.getRegistryValue(), req.getPlatform(), req.getSites(), req.getTaskIds());
req.getRegistryValue(), req.getPlatform(), req.getSites(), req.getTaskIds(), req.getFreeCloudAccountCount());
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alipay.api.config.filter;

import com.alipay.application.service.system.utils.CachedBodyHttpServletRequest;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.io.IOException;

/**
* Filter to cache request body for multiple reads
* This filter wraps POST requests with JSON content type to allow
* multiple components to read the request body without conflicts
*/
@Component
@Order(1) // Execute early in the filter chain
public class CachedBodyFilter implements Filter {

private static final Logger logger = LoggerFactory.getLogger(CachedBodyFilter.class);

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {

if (request instanceof HttpServletRequest httpRequest) {
// Only wrap POST requests with JSON content type
if ("POST".equalsIgnoreCase(httpRequest.getMethod())) {
String contentType = httpRequest.getContentType();
if (contentType != null && contentType.toLowerCase().contains("application/json")) {
try {
// Wrap the request to cache the body
CachedBodyHttpServletRequest cachedRequest = new CachedBodyHttpServletRequest(httpRequest);
logger.debug("Wrapped POST request with JSON content type for caching");
chain.doFilter(cachedRequest, response);
return;
} catch (Exception e) {
logger.warn("Failed to wrap request for body caching: {}", e.getMessage());
// Fall through to process the original request
}
}
}
}

// For non-POST requests or requests without JSON content type, proceed normally
chain.doFilter(request, response);
}

@Override
public void init(FilterConfig filterConfig) {
logger.info("CachedBodyFilter initialized");
}

@Override
public void destroy() {
logger.info("CachedBodyFilter destroyed");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alipay.api.config.filter;

import com.alipay.api.config.filter.annotation.aop.OpenApi;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.AsyncHandlerInterceptor;

/**
* 拦截器,用于检测方法上是否有@OpenApi注解,如果有则放行
*/
@Component
public class OpenApiInterceptor implements AsyncHandlerInterceptor {

private static final Logger logger = LoggerFactory.getLogger(OpenApiInterceptor.class);

/**
* 用于标记请求是否为OpenApi请求的属性名
*/
public static final String OPEN_API_REQUEST_ATTRIBUTE = "OPEN_API_REQUEST";

@Override
public boolean preHandle(HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler) {
// 对于OPTIONS请求直接放行
if (HttpMethod.OPTIONS.toString().equals(request.getMethod())) {
return true;
}

// 检查处理器是否为HandlerMethod类型
if (handler instanceof HandlerMethod handlerMethod) {
// 检查方法上是否有@OpenApi注解
if (handlerMethod.hasMethodAnnotation(OpenApi.class)) {
logger.debug("Detected @OpenApi annotation on method: {}", handlerMethod.getMethod().getName());
// 在请求属性中标记这是一个OpenApi请求
request.setAttribute(OPEN_API_REQUEST_ATTRIBUTE, Boolean.TRUE);
// 有@OpenApi注解的方法直接放行,认证由OpenApiAspect处理
return true;
}
}

// 没有@OpenApi注解的方法,交给下一个拦截器处理
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,26 @@
@Component
public class PermissionInterceptor implements AsyncHandlerInterceptor {

private static final Logger logger = LoggerFactory.getLogger(PermissionInterceptor.class);

@Resource
private UserMapper userMapper;

@Override
public boolean preHandle(HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler) {
// 对于OPTIONS请求直接放行
if (HttpMethod.OPTIONS.toString().equals(request.getMethod())) {
return true;
}

// 检查请求是否已被标记为OpenApi请求
Boolean isOpenApiRequest = (Boolean) request.getAttribute(OpenApiInterceptor.OPEN_API_REQUEST_ATTRIBUTE);
if (Boolean.TRUE.equals(isOpenApiRequest)) {
logger.info("Skipping permission check for OpenApi request");
return true;
}

// 非OpenApi请求,执行正常的权限验证
String token = request.getHeader("token");
if (StringUtils.isBlank(token) || "null".equals(token)) {
throw new UserNoLoginException("Login failed");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,17 +47,25 @@ public class WebMvcConfig implements WebMvcConfigurer {
private LocaleChangeInterceptor localeChangeInterceptor;
@Resource
private LangContextInterceptor langContextInterceptor;
@Resource
private OpenApiInterceptor openApiInterceptor;

@Override
public void addInterceptors(InterceptorRegistry registry) {
// 添加国际化拦截器
registry.addInterceptor(localeChangeInterceptor);
// 添加语言上下文拦截器
registry.addInterceptor(langContextInterceptor);
// 添加OpenApi拦截器,用于检测@OpenApi注解
registry.addInterceptor(openApiInterceptor).addPathPatterns("/api/**");
// 添加权限拦截器,但OpenApi拦截器已经处理过的请求将被放行
registry.addInterceptor(permissionInterceptor).addPathPatterns("/api/**")
.excludePathPatterns("/api/agent/**")
.excludePathPatterns("/api/user/queryUserInfo")
.excludePathPatterns("/api/user/login")
.excludePathPatterns("/api/open/v1/**")
.excludePathPatterns("/api/cloudAccount/acceptCloudAccount");
.excludePathPatterns("/api/user/register")
.excludePathPatterns("/api/tenant/checkInviteCode")
.excludePathPatterns("/api/open/v1/**");
}

@Override
Expand Down
Loading
Loading