diff --git a/modules/module-ci-machine/pom.xml b/modules/module-ci-machine/pom.xml
index ce137127..6f221741 100644
--- a/modules/module-ci-machine/pom.xml
+++ b/modules/module-ci-machine/pom.xml
@@ -28,6 +28,12 @@
3.15.1
+
+
+ org.springframework.boot
+ spring-boot-starter-websocket
+
+
com.jcraft
@@ -63,7 +69,14 @@
module-infra-biz
${revision}
-
-
+
+
+
+ org.apache.httpcomponents
+ httpclient
+ 4.5.13
+
+
+
diff --git a/modules/module-ci-machine/src/main/java/cd/casic/module/machine/Interceptor/WebSocketHandshakeInterceptor.java b/modules/module-ci-machine/src/main/java/cd/casic/module/machine/Interceptor/WebSocketHandshakeInterceptor.java
new file mode 100644
index 00000000..a0a6f890
--- /dev/null
+++ b/modules/module-ci-machine/src/main/java/cd/casic/module/machine/Interceptor/WebSocketHandshakeInterceptor.java
@@ -0,0 +1,85 @@
+package cd.casic.module.machine.Interceptor;
+
+import lombok.extern.slf4j.Slf4j;
+import org.jetbrains.annotations.NotNull;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.server.ServerHttpRequest;
+import org.springframework.http.server.ServerHttpResponse;
+import org.springframework.stereotype.Component;
+import org.springframework.web.socket.WebSocketHandler;
+import org.springframework.web.socket.server.HandshakeInterceptor;
+
+import java.net.URI;
+import java.net.URLDecoder;
+import java.nio.charset.StandardCharsets;
+import java.util.Map;
+
+import static cd.casic.framework.commons.exception.util.ServiceExceptionUtil.exception;
+import static cd.casic.module.machine.contants.MachineErrorCodeConstants.*;
+
+@Component
+@Slf4j
+public class WebSocketHandshakeInterceptor implements HandshakeInterceptor {
+ @Override
+ public boolean beforeHandshake(
+ @NotNull ServerHttpRequest request,
+ @NotNull ServerHttpResponse response,
+ @NotNull WebSocketHandler wsHandler,
+ @NotNull Map attributes
+ ) {
+ log.info("WebSocket握手请求: {}", request.getURI());
+
+ // 从URL参数中获取id
+ String id = extractIdFromUrl(request.getURI());
+
+ System.out.println("-----------------------------------------");
+ if (id != null) {
+ attributes.put("machineId", id); // 将id存入attributes
+ log.info("握手验证成功 - ID: {}", id);
+ return true;
+ } else {
+ response.setStatusCode(HttpStatus.BAD_REQUEST);
+ log.warn("握手验证失败 - 缺少 id URL 参数");
+ return false;
+ }
+ }
+
+ @Override
+ public void afterHandshake(
+ @NotNull ServerHttpRequest request,
+ @NotNull ServerHttpResponse response,
+ @NotNull WebSocketHandler wsHandler,
+ Exception exception
+ ) {
+ if (exception == null) {
+ log.info("WebSocket握手完成 - URI: {}",
+ request.getURI());
+ } else {
+ log.error("WebSocket握手异常 - URI: {}, 异常信息: {}",
+ request.getURI(),
+ exception.getMessage(),
+ exception);
+ }
+ }
+
+ // 从URI中提取id参数的辅助方法
+ private String extractIdFromUrl(URI uri) {
+ try {
+ String query = uri.getQuery();
+ if (query != null) {
+ String[] params = query.split("&");
+ for (String param : params) {
+ String[] keyValue = param.split("=");
+ if (keyValue.length == 2 && "id".equalsIgnoreCase(keyValue[0])) { // 修改为匹配id
+ return URLDecoder.decode(keyValue[1], StandardCharsets.UTF_8);
+ }
+ }
+ }
+ } catch (Exception e) {
+ log.error("解析URL参数失败", e);
+ throw exception(FAILED_TO_PARSE_URL_PARAMETERS);
+ }
+ return null;
+ }
+
+}
diff --git a/modules/module-ci-machine/src/main/java/cd/casic/module/machine/component/FileTreeComponent.java b/modules/module-ci-machine/src/main/java/cd/casic/module/machine/component/FileTreeComponent.java
new file mode 100644
index 00000000..d42fa416
--- /dev/null
+++ b/modules/module-ci-machine/src/main/java/cd/casic/module/machine/component/FileTreeComponent.java
@@ -0,0 +1,214 @@
+package cd.casic.module.machine.component;
+
+import cd.casic.module.machine.dal.model.FileNode;
+import com.jcraft.jsch.Channel;
+import com.jcraft.jsch.ChannelSftp;
+import com.jcraft.jsch.JSchException;
+import com.jcraft.jsch.Session;
+import com.jcraft.jsch.SftpATTRS;
+import com.jcraft.jsch.SftpException;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
+
+import static cd.casic.framework.commons.exception.util.ServiceExceptionUtil.exception;
+import static cd.casic.module.machine.contants.MachineErrorCodeConstants.*;
+
+@Slf4j
+@Component
+public class FileTreeComponent {
+
+ private ChannelSftp sftp;
+ //todo缓存过期还未设置
+ private final Map directoryCache = new ConcurrentHashMap<>();
+ private static final long CACHE_EXPIRE_TIME = 60 * 1000; // 缓存过期时间:1分钟
+ private final Map cacheTimeStamp = new ConcurrentHashMap<>();
+
+ // 将文件树转换为JSON友好的Map结构(仅一级目录)
+ public static Map convertToMap(FileNode node) {
+ Map map = new HashMap<>();
+ map.put("name", node.getName());
+ map.put("isDirectory", node.isDirectory());
+ map.put("size", node.getSize());
+ map.put("permissions", node.getPermissions());
+ map.put("modifiedTime", node.getModifiedTime());
+ // 仅添加直接子项,不递归
+ if (node.isDirectory() && !node.getChildren().isEmpty()) {
+ List