Bladeren bron

一大波更新
1.采用前后端分离, 删除前端相关的文件
2.使用 docker 镜像来构建源码

reghao 1 week geleden
bovenliggende
commit
1879471310
100 gewijzigde bestanden met toevoegingen van 1440 en 3027 verwijderingen
  1. 20 48
      README.md
  2. 0 8
      agent/Dockerfile
  3. 6 0
      agent/pom.xml
  4. 64 49
      agent/src/main/java/cn/reghao/devops/agent/AgentApp.java
  5. 13 4
      agent/src/main/java/cn/reghao/devops/agent/config/AgentConfig.java
  6. 0 29
      agent/src/main/java/cn/reghao/devops/agent/config/LogFile.java
  7. 0 112
      agent/src/main/java/cn/reghao/devops/agent/machine/MachineEvent.java
  8. 124 0
      agent/src/main/java/cn/reghao/devops/agent/service/AppJob.java
  9. 102 0
      agent/src/main/java/cn/reghao/devops/agent/service/DeployJob.java
  10. 50 0
      agent/src/main/java/cn/reghao/devops/agent/service/DeployPipelineContext.java
  11. 40 0
      agent/src/main/java/cn/reghao/devops/agent/service/DockerAppStat.java
  12. 0 33
      agent/src/main/java/cn/reghao/devops/agent/service/FileReader.java
  13. 0 103
      agent/src/main/java/cn/reghao/devops/agent/service/TailReader.java
  14. 0 91
      agent/src/main/java/cn/reghao/devops/agent/task/ImageCleanTask.java
  15. 0 133
      agent/src/main/java/cn/reghao/devops/agent/task/ProcScanTask.java
  16. 205 0
      agent/src/main/java/cn/reghao/devops/agent/ws/WebSocketClient.java
  17. 0 102
      agent/src/main/java/cn/reghao/devops/agent/ws/WebSocketListenerImpl.java
  18. 0 123
      agent/src/main/java/cn/reghao/devops/agent/ws/WsClient.java
  19. 0 60
      agent/src/main/java/cn/reghao/devops/agent/ws/event/EventCenter.java
  20. 0 51
      agent/src/main/java/cn/reghao/devops/agent/ws/event/handler/EvtAppDeployHandler.java
  21. 0 73
      agent/src/main/java/cn/reghao/devops/agent/ws/event/handler/EvtAppStatHandler.java
  22. 0 90
      agent/src/main/java/cn/reghao/devops/agent/ws/event/handler/EvtDockerHandler.java
  23. 1 1
      agent/src/main/resources/logback.xml
  24. 71 2
      agent/src/test/java/AgentTest.java
  25. 0 125
      agent/src/test/java/RtmpFileCleaner.java
  26. 16 20
      agent/src/test/java/WebSocketClient.java
  27. 7 7
      agent/src/test/java/WebSocketClientHandler.java
  28. 1 9
      bin/agent/devopsagent.json
  29. 3 3
      bin/mgr/devopsmgr.yml
  30. 1 1
      bin/mgr/restart.sh
  31. 1 1
      bin/mgr/shutdown.sh
  32. 1 1
      bin/mgr/start.sh
  33. 1 1
      bin/mgr/start1.sh
  34. 9 4
      common/pom.xml
  35. 0 12
      common/src/main/java/cn/reghao/devops/common/agent/app/iface/AppDeploy.java
  36. 0 20
      common/src/main/java/cn/reghao/devops/common/agent/app/iface/AppStat.java
  37. 0 28
      common/src/main/java/cn/reghao/devops/common/agent/app/iface/impl/AppDeployImpl.java
  38. 0 73
      common/src/main/java/cn/reghao/devops/common/agent/app/iface/impl/AppStatImpl.java
  39. 0 121
      common/src/main/java/cn/reghao/devops/common/agent/app/iface/impl/DockerApp.java
  40. 8 3
      common/src/main/java/cn/reghao/devops/common/docker/Docker.java
  41. 48 23
      common/src/main/java/cn/reghao/devops/common/docker/DockerImpl.java
  42. 35 0
      common/src/main/java/cn/reghao/devops/common/docker/DockerManager.java
  43. 0 130
      common/src/main/java/cn/reghao/devops/common/docker/DockerService.java
  44. 0 32
      common/src/main/java/cn/reghao/devops/common/docker/model/DockerContainer.java
  45. 53 3
      common/src/main/java/cn/reghao/devops/common/docker/model/DockerContainerConfig.java
  46. 0 29
      common/src/main/java/cn/reghao/devops/common/docker/model/DockerImage.java
  47. 0 11
      common/src/main/java/cn/reghao/devops/common/docker/model/ExposedPorts.java
  48. 0 15
      common/src/main/java/cn/reghao/devops/common/docker/model/Healthcheck.java
  49. 0 24
      common/src/main/java/cn/reghao/devops/common/docker/model/HostConfig.java
  50. 0 14
      common/src/main/java/cn/reghao/devops/common/docker/model/Labels.java
  51. 0 11
      common/src/main/java/cn/reghao/devops/common/docker/model/NetworkingConfig.java
  52. 0 21
      common/src/main/java/cn/reghao/devops/common/docker/model/RestartPolicy.java
  53. 0 15
      common/src/main/java/cn/reghao/devops/common/docker/model/Volumes.java
  54. 0 39
      common/src/main/java/cn/reghao/devops/common/machine/CPU.java
  55. 0 158
      common/src/main/java/cn/reghao/devops/common/machine/Disk.java
  56. 0 76
      common/src/main/java/cn/reghao/devops/common/machine/Machine.java
  57. 0 76
      common/src/main/java/cn/reghao/devops/common/machine/Memory.java
  58. 0 107
      common/src/main/java/cn/reghao/devops/common/machine/Network.java
  59. 0 40
      common/src/main/java/cn/reghao/devops/common/machine/OS.java
  60. 0 8
      common/src/main/java/cn/reghao/devops/common/machine/model/CPUInfo.java
  61. 0 31
      common/src/main/java/cn/reghao/devops/common/machine/model/DiskInfo.java
  62. 0 40
      common/src/main/java/cn/reghao/devops/common/machine/model/DiskPartition.java
  63. 0 28
      common/src/main/java/cn/reghao/devops/common/machine/model/MemoryInfo.java
  64. 0 27
      common/src/main/java/cn/reghao/devops/common/machine/model/MemoryStat.java
  65. 0 20
      common/src/main/java/cn/reghao/devops/common/machine/model/NetworkCard.java
  66. 0 42
      common/src/main/java/cn/reghao/devops/common/machine/model/SysProcess.java
  67. 36 0
      common/src/main/java/cn/reghao/devops/common/msg/AgentCommand.java
  68. 16 0
      common/src/main/java/cn/reghao/devops/common/msg/AgentMessage.java
  69. 25 0
      common/src/main/java/cn/reghao/devops/common/msg/AgentResponse.java
  70. 17 0
      common/src/main/java/cn/reghao/devops/common/msg/AgentResponseType.java
  71. 17 0
      common/src/main/java/cn/reghao/devops/common/msg/DeployStepName.java
  72. 0 17
      common/src/main/java/cn/reghao/devops/common/msg/MessageSender.java
  73. 0 9
      common/src/main/java/cn/reghao/devops/common/msg/constant/AppId.java
  74. 19 0
      common/src/main/java/cn/reghao/devops/common/msg/constant/StepStatus.java
  75. 30 0
      common/src/main/java/cn/reghao/devops/common/msg/constant/WsClientType.java
  76. 0 26
      common/src/main/java/cn/reghao/devops/common/msg/event/EvtAgentHeartbeat.java
  77. 17 16
      common/src/main/java/cn/reghao/devops/common/msg/event/EvtAgentStart.java
  78. 1 0
      common/src/main/java/cn/reghao/devops/common/msg/event/EvtAppDeploy.java
  79. 9 3
      common/src/main/java/cn/reghao/devops/common/msg/event/EvtAppStat.java
  80. 13 8
      common/src/main/java/cn/reghao/devops/common/msg/event/EvtAppStatResult.java
  81. 0 10
      common/src/main/java/cn/reghao/devops/common/msg/event/EvtBuildApp.java
  82. 0 10
      common/src/main/java/cn/reghao/devops/common/msg/event/EvtBuildAppResult.java
  83. 0 31
      common/src/main/java/cn/reghao/devops/common/msg/event/EvtDockerOps.java
  84. 0 26
      common/src/main/java/cn/reghao/devops/common/msg/event/EvtDockerOpsResult.java
  85. 0 38
      common/src/main/java/cn/reghao/devops/common/msg/event/EvtTaskResult.java
  86. 77 0
      common/src/main/java/cn/reghao/devops/common/util/Disk.java
  87. 77 0
      common/src/main/java/cn/reghao/devops/common/util/DiskDetail.java
  88. 56 0
      common/src/main/java/cn/reghao/devops/common/util/JsonUtils.java
  89. 97 0
      common/src/main/java/cn/reghao/devops/common/util/Machine.java
  90. 0 90
      common/src/main/java/cn/reghao/devops/common/ws/WebSocketListenerImpl.java
  91. 0 106
      common/src/main/java/cn/reghao/devops/common/ws/WsClient.java
  92. 0 7
      mgr/Dockerfile
  93. 1 1
      mgr/pom.xml
  94. 4 7
      mgr/src/main/java/cn/reghao/devops/mgr/admin/service/HomeViewService.java
  95. 0 1
      mgr/src/main/java/cn/reghao/devops/mgr/config/AccessInterceptor.java
  96. 3 28
      mgr/src/main/java/cn/reghao/devops/mgr/config/spring/BeanConfig.java
  97. 3 5
      mgr/src/main/java/cn/reghao/devops/mgr/config/spring/SpringLifecycle.java
  98. 23 14
      mgr/src/main/java/cn/reghao/devops/mgr/ops/app/controller/BuildDeployController.java
  99. 1 2
      mgr/src/main/java/cn/reghao/devops/mgr/ops/app/db/query/AppDeployQuery.java
  100. 18 11
      mgr/src/main/java/cn/reghao/devops/mgr/ops/app/db/query/impl/AppBuildQueryImpl.java

+ 20 - 48
README.md

@@ -1,62 +1,34 @@
 # devops
-freemarker 模板来自[这个项目](https://github.com/langhsu/mblog) master 分支的 da0aec93 版本
-
 ## 依赖
 - os:Linux
-- jdk:11
+- jdk:17
 - maven:3.9
-- mysql:5.7
+- mysql:8.0
 
 ## 模块
 devops 项目模块:
-- web
-> 监听 4030 HTTP 端口和 14030 RPC 端口
->
-> - console 模块
-> > 作为 [oss](https://git.reghao.cn/reghao/oss) 项目的 oss-console 模块
-> - blog 模块
-> > 一个独立的 blog 服务
-> - devops 模块
-> > 一个独立的 devops 服务
-
-## 构建部署
-### pull 项目源码
-```
-cd ~/Downloads
-git clone https://git.reghao.cn/reghao/devops.git
-```
+- common
+> 公共依赖
+- mgr
+> 构建管理端,监听 4030 端口
+- agent
+> 部署监控端,通过 websocket 和 mgr 通信
 
-### 初始化 MySQL
+## 构建
+安装依赖
 ```
-cd ~/Downloads/devops/zzz
-bash db_init.sh
+git clone https://git.reghao.cn/reghao/jutil
+cd jutil
+mvn clean install -Dmaven.test.skip=true
 ```
-> 执行 db_init.sh 会自动创建 devops 项目需要的数据库并初始化数据库表
-> > 需要自行指定 db_init.sh 脚本中的 host, username, password 等变量值
 
-### 构建并运行
+构建应用
 ```
-cd ~/Downloads/devops/zzz
-bash build_jar.sh
+mvn clean package -Dmaven.test.skip
 ```
-> 执行 db_init.sh 会自动构建并运行 devopsweb 应用 jar 包
-> > build_jar.sh 脚本中的 proj_dir 变量默认是 ~/Downloads/devops
+> 执行上述命令后, 会在 bin/mgr 和 bin/agent 目录里生成 devops-mgr.jar 和 devops-agent.jar 两个文件
+> > devops-mgr 需要修改 bin/mgr/devopsmgr.yml 中的数据库连接 
+> > 
+> > devops-agent 需要修改 bin/agent/devopsagent.json 中的 devops-mgr 的地址和端口
 >
-> mvn 构建默认使用 dev 环境, 所以需要修改 web 模块 resources/application-dev.yml 配置文件中的第三方服务配置
->
-> 运行 devopsweb 应用前需要根据环境修改 devops/web/bin/devopsweb.yml 文件中的配置
-> > mysql 配置
->
-> 运行应用
-> > 执行 devops/web/bin/start.sh 脚本
-> > > 使用 devopsweb.yml 外部配置文件
-> >
-> > 执行 devops/web/bin/start1.sh 脚本
-> > > 根据 mvn 打包选择的 profile, 使用 resources/application-dev.yml 或 resources/application-test.yml 内部配置文件
-
-### 停止应用
-```
-cd ~/Downloads/devops/web/bin
-bash shutdown.sh
-```
-> 执行 shutdown.sh 会结束 devopsweb 应用
+> 然后在相应目录中执行 start.sh 和 shutdown.sh 两个脚本可以分配启动和结束应用

+ 0 - 8
agent/Dockerfile

@@ -1,8 +0,0 @@
-FROM adoptopenjdk/openjdk11:x86_64-debianslim-jdk-11.0.24_8-slim
-
-WORKDIR /app
-RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 'Asia/Shanghai' >/etc/timezone
-COPY bin/devops-agent.jar /app/devops-agent.jar
-COPY bin/agent.json /app/agent.json
-
-ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-Djava.awt.headless=true","-jar","/app/devops-agent.jar", "agent.json"]

+ 6 - 0
agent/pom.xml

@@ -22,6 +22,12 @@
             <artifactId>common</artifactId>
             <version>1.0.0-SNAPSHOT</version>
         </dependency>
+
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+            <version>2.15.2</version>
+        </dependency>
     </dependencies>
 
     <build>

+ 64 - 49
agent/src/main/java/cn/reghao/devops/agent/AgentApp.java

@@ -5,45 +5,31 @@ import ch.qos.logback.classic.Logger;
 import ch.qos.logback.classic.LoggerContext;
 import cn.reghao.devops.agent.config.ConfigFile;
 import cn.reghao.devops.agent.config.AgentConfig;
-import cn.reghao.devops.agent.service.FileReader;
-import cn.reghao.devops.agent.ws.WsClient;
-import cn.reghao.devops.common.agent.app.iface.AppDeploy;
-import cn.reghao.devops.common.agent.app.iface.AppStat;
-import cn.reghao.devops.common.agent.app.iface.impl.AppDeployImpl;
-import cn.reghao.devops.common.agent.app.iface.impl.AppStatImpl;
-import cn.reghao.devops.common.agent.app.iface.impl.DockerApp;
+import cn.reghao.devops.agent.ws.WebSocketClient;
 import cn.reghao.devops.common.docker.Docker;
 import cn.reghao.devops.common.docker.DockerImpl;
-import cn.reghao.devops.common.msg.MessageSender;
+import cn.reghao.devops.common.docker.DockerManager;
 import cn.reghao.jutil.jdk.serializer.JsonConverter;
 import cn.reghao.jutil.jdk.string.StringRegexp;
 import cn.reghao.jutil.jdk.thread.ThreadPoolWrapper;
-import cn.reghao.jutil.jdk.util.SingleInstance;
 import lombok.extern.slf4j.Slf4j;
 import org.slf4j.LoggerFactory;
 
 import java.io.File;
 import java.io.IOException;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.Socket;
+import java.net.*;
 import java.nio.charset.StandardCharsets;
 import java.util.concurrent.ScheduledExecutorService;
 
 @Slf4j
 public class AgentApp {
-	public static MessageSender messageSender;
-	public static ScheduledExecutorService scheduler = ThreadPoolWrapper.scheduledThreadPool("heartbeat", 5);
-	public static Docker docker = new DockerImpl();
-
 	static void setLogLevel() {
 		LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
 		Logger rootLogger = loggerContext.getLogger("ROOT");
 		rootLogger.setLevel(Level.INFO);
-		//LoggerConfig.initLogger(Appenders.fileAppender());
 	}
-	
-	static boolean tryConnect(String host, int port) {
+
+	private static void tryConnect(String host, int port) {
 		try {
 			boolean isAddress = StringRegexp.matchIPv4Address(host);
 			InetAddress inetAddress;
@@ -56,36 +42,55 @@ public class AgentApp {
 			InetSocketAddress socketAddress = new InetSocketAddress(inetAddress, port);
 			Socket socket = new Socket();
 			socket.connect(socketAddress);
-			return true;
 		} catch (IOException e) {
-			log.error("尝试建立到 {}:{} 的 Socket 连接失败", host, port);
+			String errorMsg = String.format("尝试建立到 %s:%s 的 Socket 连接失败", host, port);
+			throw  new RuntimeException(errorMsg);
 		}
-		return false;
 	}
 
-	static MessageSender getMessageSender(AgentConfig agentConfig) {
-		DockerApp dockerApp = new DockerApp(docker);
-		AppDeploy appDeploy = new AppDeployImpl(dockerApp);
-		AppStat appStat = new AppStatImpl(dockerApp);
-		FileReader fileReader = new FileReader(agentConfig.getLogFiles());
+	private static void shutdownGracefully(WebSocketClient webSocketClient) {
+		Runtime.getRuntime().addShutdownHook(new Thread(() -> {
+			log.info("收到退出信号,正在执行清理...");
+			webSocketClient.stop(); // 关闭 WebSocket,停止任务队列
+			log.info("资源清理完成,结束 devops-agent...");
+		}));
+	}
 
-		if (tryConnect(agentConfig.getHost(), agentConfig.getPort())) {
-			messageSender = new WsClient(agentConfig, fileReader, appDeploy, appStat);
-			return messageSender;
-		}
+	private static boolean ensureSingleInstance() {
+		try {
+			// 绑定到 localhost,避免被外部网络扫描或占用
+			ServerSocket lockSocket = new ServerSocket(60001, 0, InetAddress.getByName("127.0.0.1"));
 
-		return null;
+			// 设置守护线程或钩子,在进程退出时关闭 Socket(虽然系统会自动回收)
+			Runtime.getRuntime().addShutdownHook(new Thread(() -> {
+				try {
+					if (lockSocket != null) lockSocket.close();
+				} catch (Exception ignored) {}
+			}));
+			return true;
+		} catch (BindException e) {
+			// 端口已被占用
+			return false;
+		} catch (IOException e) {
+			System.err.println("端口检测发生未知错误: " + e.getMessage());
+			return false;
+		}
 	}
 
-	static void shutdownGracefully() {
-		Runtime.getRuntime().addShutdownHook(new Thread(new ShutdownHook(), "main-shutdown-hook"));
+	private static void keepAlive() {
+		try {
+			Thread.currentThread().join();
+		} catch (InterruptedException e) {
+			Thread.currentThread().interrupt();
+		}
 	}
 
-	static class ShutdownHook implements Runnable {
-		@Override
-		public void run() {
-			messageSender.close();
-			log.info("资源清理完成,结束 devops-agent...");
+	private static void checkEnvironment() {
+		// 尝试调用 docker ps,若失败则 Agent 启动无意义
+		try {
+			DockerManager.getDocker().ping();
+		} catch (Exception e) {
+			throw new RuntimeException("无法连接本地 Docker 守护进程,请检查 /var/run/docker.sock");
 		}
 	}
 
@@ -96,17 +101,27 @@ public class AgentApp {
 			return;
 		}
 
-		String configFilePath = ConfigFile.configFilePath(args[0], AgentApp.class);
-		AgentConfig agentConfig = JsonConverter.jsonFileToObject(new File(configFilePath), AgentConfig.class);
-		messageSender = getMessageSender(agentConfig);
-		if (messageSender == null) {
-			log.error("没有可用的 MessageSender, Agent 结束运行, 具体信息请查看日志");
-			System.exit(-1);
-		} else {
-			messageSender.connect();
+		if (!ensureSingleInstance()) {
+			log.error("❌ 启动失败:检测到另一个 Agent 实例已在运行。");
+			System.exit(1);
 		}
 
-		shutdownGracefully();
-		SingleInstance.onlyOne(60001);
+		try {
+			// 1. 环境预检 (Docker 是否可用)
+			checkEnvironment();
+
+			String configFilePath = ConfigFile.configFilePath(args[0], AgentApp.class);
+			AgentConfig agentConfig = JsonConverter.jsonFileToObject(new File(configFilePath), AgentConfig.class);
+			// 4. 启动核心服务 (心跳、监控、WebSocket)
+			WebSocketClient webSocketClient = new WebSocketClient(agentConfig);
+			tryConnect(agentConfig.getHost(), agentConfig.getPort());
+			webSocketClient.start();
+			shutdownGracefully(webSocketClient);
+			// 6. 阻塞主线程,防止应用退出
+			keepAlive();
+		} catch (Exception e) {
+			log.error("Agent 启动失败,即将退出: {}", e.getMessage());
+			System.exit(1);
+		}
 	}
 }

+ 13 - 4
agent/src/main/java/cn/reghao/devops/agent/config/AgentConfig.java

@@ -1,10 +1,11 @@
 package cn.reghao.devops.agent.config;
 
+import cn.reghao.devops.common.util.Machine;
 import lombok.AllArgsConstructor;
 import lombok.Getter;
 import lombok.Setter;
 
-import java.util.List;
+import java.util.UUID;
 
 /**
  * @author reghao
@@ -14,9 +15,17 @@ import java.util.List;
 @Setter
 @Getter
 public class AgentConfig {
-    private String protocol;
+    private String agentId;
     private String host;
     private int port;
-    private boolean logstash;
-    private List<LogFile> logFiles;
+    private String serverUrl;
+    private String dockerHost;
+    private String token;
+
+    public AgentConfig() {
+        this.agentId = Machine.ID;
+        this.serverUrl = "ws://localhost:4030";
+        this.dockerHost = "unix:///var/run/docker.sock";
+        this.token = UUID.randomUUID().toString();
+    }
 }

+ 0 - 29
agent/src/main/java/cn/reghao/devops/agent/config/LogFile.java

@@ -1,29 +0,0 @@
-package cn.reghao.devops.agent.config;
-
-import lombok.AllArgsConstructor;
-
-/**
- * @author reghao
- * @date 2022-05-20 18:28:21
- */
-@AllArgsConstructor
-public class LogFile {
-    private String domain;
-    private String filePath;
-
-    public void setDomain(String domain) {
-        this.domain = domain;
-    }
-
-    public String getDomain() {
-        return domain;
-    }
-
-    public void setFilePath(String filePath) {
-        this.filePath = filePath;
-    }
-
-    public String getFilePath() {
-        return filePath;
-    }
-}

+ 0 - 112
agent/src/main/java/cn/reghao/devops/agent/machine/MachineEvent.java

@@ -1,112 +0,0 @@
-package cn.reghao.devops.agent.machine;
-
-import cn.reghao.devops.agent.AgentApp;
-import cn.reghao.devops.agent.task.ImageCleanTask;
-import cn.reghao.devops.agent.task.ProcScanTask;
-import cn.reghao.devops.common.agent.app.iface.AppStat;
-import cn.reghao.devops.common.docker.Docker;
-import cn.reghao.devops.common.machine.Machine;
-import cn.reghao.devops.common.msg.MessageSender;
-import cn.reghao.devops.common.msg.event.EvtAgentHeartbeat;
-import cn.reghao.devops.common.msg.event.EvtAgentStart;
-import cn.reghao.devops.common.msg.event.EvtAppStatResult;
-import cn.reghao.jutil.jdk.event.message.EventMessage;
-import cn.reghao.jutil.jdk.thread.ThreadPoolWrapper;
-import lombok.extern.slf4j.Slf4j;
-
-import java.io.IOException;
-import java.util.List;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.TimeUnit;
-
-/**
- * @author reghao
- * @date 2021-09-03 09:22:42
- */
-@Slf4j
-public class MachineEvent {
-    private final ScheduledExecutorService scheduler;
-    private ScheduledFuture<?> heartbeatFuture;
-    private final Machine machine;
-    private final AppStat appStat;
-    private final MessageSender messageSender;
-    private final Docker docker;
-    
-    public MachineEvent(MessageSender messageSender, Machine machine, AppStat appStat) {
-        this.messageSender = messageSender;
-        this.machine = machine;
-        this.appStat = appStat;
-        this.scheduler = AgentApp.scheduler;
-        this.docker = AgentApp.docker;
-    }
-
-    public void agentStart() {
-        EvtAgentStart evtAgentStart = machine.detail();
-        EventMessage eventMessage = EventMessage.evt(evtAgentStart);
-        pub(eventMessage);
-
-        try {
-            List<EvtAppStatResult> list = appStat.stat();
-            list.forEach(evtAppStatResult -> {
-                EventMessage eventMessage1 = EventMessage.evt(evtAppStatResult);
-                pub(eventMessage1);
-            });
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-
-        startTask();
-        agentHeartbeat();
-    }
-
-    public void agentShutdown() {
-        ThreadPoolWrapper.shutdownScheduler(scheduler);
-        EvtAgentHeartbeat machineStat = machine.stat();
-        EventMessage eventMessage = EventMessage.evt(machineStat);
-        pub(eventMessage);
-    }
-
-    public void agentHeartbeat() {
-        // 每 60s 发送一次心跳
-        int heartbeatInterval = 60;
-        heartbeatFuture = scheduler.scheduleAtFixedRate(new Heartbeat(), 5, heartbeatInterval, TimeUnit.SECONDS);
-    }
-
-    public void pauseHeartbeat() {
-        if (!heartbeatFuture.isCancelled()) {
-            heartbeatFuture.cancel(true);
-        }
-    }
-
-    private void pub(EventMessage eventMessage) {
-        if (messageSender.isConnected()) {
-            try {
-                messageSender.send("", eventMessage);
-            } catch (IOException e) {
-                e.printStackTrace();
-            }
-        } else {
-            log.error("连接断开");
-        }
-    }
-
-    class Heartbeat implements Runnable {
-        @Override
-        public void run() {
-            EvtAgentHeartbeat machineStat = machine.stat();
-            EventMessage eventMessage = EventMessage.evt(machineStat);
-            pub(eventMessage);
-        }
-    }
-
-    public void startTask() {
-        ProcScanTask procScanTask = new ProcScanTask(messageSender, docker);
-        scheduler.schedule(procScanTask, 0, TimeUnit.MINUTES);
-        scheduler.scheduleAtFixedRate(procScanTask, 1, 8, TimeUnit.HOURS);
-
-        ImageCleanTask cleanTask = new ImageCleanTask(messageSender, docker);
-        scheduler.schedule(cleanTask, 1, TimeUnit.MINUTES);
-        scheduler.scheduleAtFixedRate(cleanTask, 2, 8, TimeUnit.HOURS);
-    }
-}

+ 124 - 0
agent/src/main/java/cn/reghao/devops/agent/service/AppJob.java

@@ -0,0 +1,124 @@
+package cn.reghao.devops.agent.service;
+
+import cn.reghao.devops.common.msg.AgentResponse;
+import cn.reghao.devops.common.msg.AgentResponseType;
+import cn.reghao.devops.common.msg.DeployStepName;
+import cn.reghao.devops.common.docker.DockerManager;
+import cn.reghao.devops.common.msg.constant.AppStatOps;
+import cn.reghao.devops.common.msg.constant.PackType;
+import cn.reghao.devops.common.msg.event.EvtAppStat;
+import cn.reghao.devops.common.msg.event.EvtAppStatResult;
+import cn.reghao.devops.common.util.JsonUtils;
+import cn.reghao.devops.common.util.Machine;
+import com.github.dockerjava.api.command.InspectContainerResponse;
+import lombok.extern.slf4j.Slf4j;
+
+import java.net.http.WebSocket;
+
+/**
+ * @author reghao
+ * @date 2026-03-12 14:28:39
+ */
+@Slf4j
+public class AppJob {
+    public void handleApp(WebSocket webSocket, EvtAppStat evtAppStat) {
+        String appId = evtAppStat.getAppId();
+        String ops = evtAppStat.getOps();
+        EvtAppStatResult statResult = new EvtAppStatResult(appId, Machine.ID);
+
+        String packType = evtAppStat.getPackType();
+        if (!packType.equals(PackType.docker.name())) {
+            String errorMsg = String.format("不支持 %s 打包类型", packType);
+            statResult.setDeployStepName(DeployStepName.DEPLOY_DONE.name());
+            statResult.setErrorMsg(errorMsg);
+            sendResponse(webSocket, statResult);
+            return;
+        }
+
+        try {
+            InspectContainerResponse response = null;
+            switch (AppStatOps.valueOf(ops)) {
+                case stat:
+                    response = DockerManager.getDocker().inspectContainer(evtAppStat.getAppId());
+                    DockerAppStat.setStat(statResult, response);
+                    sendResponse(webSocket, statResult);
+                    return;
+                case stop:
+                    response = DockerManager.getDocker().stop(evtAppStat.getAppId());
+                    DockerAppStat.setStat(statResult, response);
+                    sendResponse(webSocket, statResult);
+                    return;
+                case start:
+                    DockerManager.getDocker().start(evtAppStat.getAppId());
+                    break;
+                case restart:
+                    DockerManager.getDocker().restart(evtAppStat.getAppId());
+                    break;
+                default:
+                    String msg = String.format("应用状态操作类型 %s 不存在", ops);
+                    statResult.setErrorMsg(msg);
+                    statResult.setDeployStepName(DeployStepName.DEPLOY_DONE.name());
+                    sendResponse(webSocket, statResult);
+                    return;
+            }
+
+            statResult.setDeployStepName(DeployStepName.HEALTH_CHECK.name());
+            boolean isHealth = checkHealth(webSocket, statResult);
+            response = DockerManager.getDocker().inspectContainer(appId);
+            DockerAppStat.setStat(statResult, response);
+            if (isHealth) {
+                statResult.setDeployStepName(DeployStepName.DEPLOY_DONE.name());
+                sendResponse(webSocket, statResult);
+            } else {
+                statResult.setDeployStepName(DeployStepName.DEPLOY_DONE.name());
+                sendResponse(webSocket, statResult);
+            }
+        } catch (Exception e) {
+            statResult.setDeployStepName(DeployStepName.DEPLOY_DONE.name());
+            statResult.setErrorMsg(e.getMessage());
+
+            AgentResponse<Object> resp = new AgentResponse<>();
+            resp.setAgentId(Machine.ID);
+            resp.setTaskId(appId);
+            resp.setType(AgentResponseType.APP_TASK_ACK);
+            resp.setData(statResult);
+
+            String json = JsonUtils.toJson(resp);
+            if (webSocket != null && !webSocket.isOutputClosed()) {
+                webSocket.sendText(json, true).whenComplete((ws, ex) -> {
+                    if (ex != null) {
+                        log.error("发送失败,这可能导致输出流被标记为关闭!", ex);
+                        // 这里可以触发重连
+                    }
+                });
+            }
+        }
+    }
+
+    private boolean checkHealth(WebSocket webSocket, EvtAppStatResult evtAppStatResult) {
+        int maxRetries = 15; // 假设尝试 15 次,每次 2s
+        for (int i = 0; i < maxRetries; i++) {
+            sendResponse(webSocket, evtAppStatResult);
+            InspectContainerResponse inspect = DockerManager.getDocker().inspectContainer(evtAppStatResult.getAppId());
+
+            // 如果你配置了 Docker HealthCheck
+            //String status = inspect.getState().getHealth().getStatus();
+            //if ("healthy".equals(status)) return true;
+
+            // 如果没配置 HealthCheck,只需判断(容器启动 10s 后的) Running 状态
+            if (Boolean.TRUE.equals(inspect.getState().getRunning()) && i > 4) return true;
+
+            try { Thread.sleep(2000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
+        }
+        return false;
+    }
+
+    private void sendResponse(WebSocket webSocket, EvtAppStatResult evtAppStatResult) {
+        AgentResponse<Object> resp = new AgentResponse<>();
+        resp.setAgentId(Machine.ID);
+        resp.setTaskId(evtAppStatResult.getAppId());
+        resp.setType(AgentResponseType.APP_TASK_ACK);
+        resp.setData(evtAppStatResult);
+        webSocket.sendText(JsonUtils.toJson(resp), true);
+    }
+}

+ 102 - 0
agent/src/main/java/cn/reghao/devops/agent/service/DeployJob.java

@@ -0,0 +1,102 @@
+package cn.reghao.devops.agent.service;
+
+import cn.reghao.devops.common.msg.DeployStepName;
+import cn.reghao.devops.common.docker.DockerManager;
+import cn.reghao.devops.common.docker.model.DockerContainerConfig;
+import cn.reghao.devops.common.msg.constant.StepStatus;
+import cn.reghao.devops.common.util.Machine;
+import cn.reghao.devops.common.msg.event.EvtAppDeploy;
+import cn.reghao.devops.common.msg.event.EvtAppStatResult;
+import cn.reghao.jutil.jdk.serializer.JsonConverter;
+import com.github.dockerjava.api.command.InspectContainerResponse;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.http.WebSocket;
+
+/**
+ * @author reghao
+ * @date 2026-03-11 17:59:55
+ */
+@Slf4j
+public class DeployJob  {
+    public void handleDeploy(WebSocket webSocket, EvtAppDeploy task) {
+        String appId = task.getAppId();
+        int appPort = task.getAppPort();
+        EvtAppStatResult evtAppStatResult = new EvtAppStatResult(appId, Machine.ID);
+        DeployPipelineContext ctx = new DeployPipelineContext(webSocket, evtAppStatResult);
+        try (ctx) {
+            log.info(">>> [Pipeline] 启动流水线,应用 ID: " + appId);
+            runStep(ctx, DeployStepName.PULL_IMAGE.name(), () -> {
+                log.info(">>> [Step 1] 正在拉取镜像...");
+                DockerManager.getDocker().pull(task.getPackagePath(), task.getDockerAuth());
+            });
+
+            runStep(ctx, DeployStepName.STOP_REMOVE_OLD.name(), () -> {
+                log.info(">>> [Step 2] 正在创建容器...");
+                // 尝试删除同名旧容器
+                DockerManager.getDocker().stopAndDelete(appId);
+            });
+
+            runStep(ctx, DeployStepName.START_NEW.name(), () -> {
+                log.info(">>> [Step 3] 正在启动容器...");
+                DockerContainerConfig containerConfig = JsonConverter.jsonToObject(task.getStartScript(), DockerContainerConfig.class);
+                if (containerConfig == null) {
+                    containerConfig = new DockerContainerConfig(task.getPackagePath());
+                } else {
+                    containerConfig.setImage(task.getPackagePath());
+                    if (containerConfig.getHostConfig() == null) {
+                        containerConfig.setHostConfig(new DockerContainerConfig.HostConfig());
+                    }
+                }
+
+                DockerManager.getDocker().createAndRun(appId, containerConfig);
+            });
+
+            log.info(">>> [Step 4] 正在轮询健康检查...");
+            int maxRetries = 5; // 假设尝试 5 次,每次 2s
+            for (int i = 0; i < maxRetries; i++) {
+                runStep(ctx, DeployStepName.HEALTH_CHECK.name(), () -> {
+                    try (Socket socket = new Socket()) {
+                        // 仅检测 TCP 端口是否开放,无需 curl
+                        String host = "localhost";
+                        socket.connect(new InetSocketAddress(host, appPort), 2000);
+                        //healthCheck = true;
+                    } catch (IOException e) {
+                        throw new RuntimeException(e);
+                    }
+                });
+                try { Thread.sleep(2000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
+            }
+
+            runStep(ctx, DeployStepName.DEPLOY_DONE.name(), () -> {
+                log.info(">>> [Step 5] 上报部署状态...");
+                InspectContainerResponse inspectContainerResponse = DockerManager.getDocker().inspectContainer(appId);
+                DockerAppStat.setStat(evtAppStatResult, inspectContainerResponse);
+            });
+        } catch (Exception e) {
+            // ignore
+        }
+    }
+
+    private void runStep(DeployPipelineContext ctx, String stepName, StepExecutor executor) {
+        try {
+            ctx.updateStep(stepName, StepStatus.RUNNING.name(), null);
+            executor.execute();
+            ctx.updateStep(stepName, StepStatus.SUCCESS.name(), null);
+        } catch (Exception e) {
+            ctx.updateStep(stepName, StepStatus.FAILURE.name(), e.getMessage());
+            if (!stepName.equals(DeployStepName.HEALTH_CHECK.name())) {
+                // 抛出异常以中断后续阶段
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    @FunctionalInterface
+    interface StepExecutor {
+        void execute() throws Exception;
+    }
+}

+ 50 - 0
agent/src/main/java/cn/reghao/devops/agent/service/DeployPipelineContext.java

@@ -0,0 +1,50 @@
+package cn.reghao.devops.agent.service;
+
+import cn.reghao.devops.common.msg.AgentResponse;
+import cn.reghao.devops.common.msg.AgentResponseType;
+import cn.reghao.devops.common.msg.DeployStepName;
+import cn.reghao.devops.common.msg.constant.StepStatus;
+import cn.reghao.devops.common.msg.event.EvtAppStatResult;
+import cn.reghao.devops.common.util.JsonUtils;
+import cn.reghao.devops.common.util.Machine;
+
+import java.net.http.WebSocket;
+
+/**
+ * @author reghao
+ * @date 2026-03-13 11:10:45
+ */
+public class DeployPipelineContext implements AutoCloseable {
+    private final WebSocket webSocket;
+    private final EvtAppStatResult evtAppStatResult;
+
+    public DeployPipelineContext(WebSocket webSocket, EvtAppStatResult evtAppStatResult) {
+        this.webSocket = webSocket;
+        this.evtAppStatResult = evtAppStatResult;
+    }
+
+    /**
+     * 更新或创建阶段状态
+     */
+    public void updateStep(String stepName, String status, String errorMsg) {
+        evtAppStatResult.setDeployStepName(stepName);
+        evtAppStatResult.setDeployStepStatus(status);
+        evtAppStatResult.setErrorMsg(errorMsg);
+        if (stepName.equals(DeployStepName.DEPLOY_DONE.name())
+                && status.equals(StepStatus.SUCCESS.name())
+                && !evtAppStatResult.getRunning()) {
+            evtAppStatResult.setDeployStepName(DeployStepName.DEPLOY_DONE.name());
+        }
+
+        AgentResponse<Object> resp = new AgentResponse<>();
+        resp.setAgentId(Machine.ID);
+        resp.setTaskId(evtAppStatResult.getAppId());
+        resp.setType(AgentResponseType.DEPLOY_TASK_ACK);
+        resp.setData(evtAppStatResult);
+        webSocket.sendText(JsonUtils.toJson(resp), true);
+    }
+
+    @Override
+    public void close() {
+    }
+}

+ 40 - 0
agent/src/main/java/cn/reghao/devops/agent/service/DockerAppStat.java

@@ -0,0 +1,40 @@
+package cn.reghao.devops.agent.service;
+
+import cn.reghao.devops.common.msg.event.EvtAppStatResult;
+import cn.reghao.jutil.jdk.converter.DateTimeConverter;
+import cn.reghao.jutil.jdk.web.result.NotAvailable;
+import com.github.dockerjava.api.command.InspectContainerResponse;
+
+import java.time.LocalDateTime;
+
+/**
+ * @author reghao
+ * @date 2026-03-12 14:59:14
+ */
+public class DockerAppStat {
+    public static void setStat(EvtAppStatResult appStatResult, InspectContainerResponse containerInfo) {
+        String image = containerInfo.getConfig().getImage();
+        if (image != null && image.contains(":")) {
+            appStatResult.setCommitId(image.split(":")[1]);
+        } else {
+            appStatResult.setCommitId(NotAvailable.na.getDesc());
+        }
+
+        InspectContainerResponse.ContainerState state = containerInfo.getState();
+        if (state == null || Boolean.FALSE.equals(state.getRunning())) {
+            appStatResult.setStartTime(-1);
+            appStatResult.setPid(0);
+        } else {
+            Integer restartCount = containerInfo.getRestartCount();
+            Boolean restarting = state.getRestarting();
+            if (!Boolean.TRUE.equals(restarting)) {
+                String startedAt = state.getStartedAt();
+                LocalDateTime localDateTime = DateTimeConverter.localDateTime(startedAt);
+                long timestamp = DateTimeConverter.msTimestamp(localDateTime);
+                appStatResult.setRunning(true);
+                appStatResult.setStartTime(timestamp);
+                appStatResult.setPid(state.getPidLong().intValue());
+            }
+        }
+    }
+}

+ 0 - 33
agent/src/main/java/cn/reghao/devops/agent/service/FileReader.java

@@ -1,33 +0,0 @@
-package cn.reghao.devops.agent.service;
-
-import cn.reghao.devops.agent.config.LogFile;
-import cn.reghao.devops.agent.ws.WsClient;
-import cn.reghao.jutil.jdk.thread.ThreadPoolWrapper;
-
-import java.util.List;
-import java.util.concurrent.ExecutorService;
-
-/**
- * @author reghao
- * @date 2022-05-20 16:23:34
- */
-public class FileReader {
-    private final ExecutorService threadPool = ThreadPoolWrapper.threadPool("logstash");
-    private final List<LogFile> logFiles;
-
-    public FileReader(List<LogFile> logFiles) {
-        this.logFiles = logFiles;
-    }
-
-    public void start(WsClient wsClient) {
-        for (LogFile logFile: logFiles) {
-            String filePath = logFile.getFilePath();
-            try {
-                TailReader tailReader = new TailReader(filePath, wsClient);
-                threadPool.submit(tailReader);
-            } catch (Exception e) {
-                e.printStackTrace();
-            }
-        }
-    }
-}

+ 0 - 103
agent/src/main/java/cn/reghao/devops/agent/service/TailReader.java

@@ -1,103 +0,0 @@
-package cn.reghao.devops.agent.service;
-
-import cn.reghao.devops.agent.ws.WsClient;
-import cn.reghao.jutil.jdk.io.TextFile;
-import cn.reghao.jutil.jdk.serializer.JsonConverter;
-import cn.reghao.jutil.jdk.web.log.NginxLog;
-import lombok.extern.slf4j.Slf4j;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.RandomAccessFile;
-import java.util.List;
-import java.util.Queue;
-import java.util.concurrent.LinkedBlockingQueue;
-
-/**
- * @author reghao
- * @date 2022-05-20 11:41:08
- */
-@Slf4j
-public class TailReader implements Runnable {
-    private final String filePath;
-    private final RandomAccessFile raf;
-    private long pointer;
-    private final WsClient wsClient;
-    private final TextFile textFile;
-    private final String errorLogPath;
-
-    public TailReader(String filePath, WsClient wsClient) throws FileNotFoundException {
-        this.filePath = filePath;
-        this.raf  = new RandomAccessFile(filePath, "r");
-        this.pointer = 0;
-        this.wsClient = wsClient;
-        this.textFile = new TextFile();
-        this.errorLogPath = System.getProperty("user.dir") + "/error.log";
-    }
-
-    @Override
-    public void run() {
-        try {
-            File errLogFile = new File(errorLogPath);
-            if (!errLogFile.exists()) {
-                errLogFile.createNewFile();
-            }
-
-            while (!Thread.interrupted()) {
-                try {
-                    long length = raf.length();
-                    if (length > pointer) {
-                        raf.seek(pointer);
-                        String line = raf.readLine();
-                        while (line != null) {
-                            parseAndPersist(line, errLogFile);
-                            line = raf.readLine();
-                        }
-                        pointer = length;
-                    } else {
-                        log.info("已读取到 {} 文件末尾, 休眠 10s 后再尝试读取...", filePath);
-                        Thread.sleep(10_000);
-                    }
-                } catch (Exception e) {
-                    e.printStackTrace();
-                }
-            }
-        } catch (Exception e ) {
-            e.printStackTrace();
-        }
-    }
-
-    Queue<NginxLog> queue = new LinkedBlockingQueue<>();
-    private void parseAndPersist(String line, File errLogFile) throws InterruptedException {
-        NginxLog nginxLog = null;
-        try {
-            nginxLog = JsonConverter.jsonToObject(line, NginxLog.class);
-        } catch (Exception e) {
-            e.printStackTrace();
-            try {
-                textFile.append(errLogFile.getAbsolutePath(), List.of(line));
-            } catch (IOException ex) {
-                ex.printStackTrace();
-            }
-        }
-
-        if (nginxLog != null) {
-            if (!wsClient.isConnected()) {
-                log.info("websocket disconnected, wait 10s...");
-                Thread.sleep(10_000);
-            }
-
-            try {
-                NginxLog nginxLog1 = queue.poll();
-                while (nginxLog1 != null) {
-                    wsClient.send("", nginxLog);
-                    nginxLog1 = queue.poll();
-                }
-                wsClient.send("", nginxLog);
-            } catch (Exception e) {
-                queue.add(nginxLog);
-            }
-        }
-    }
-}

+ 0 - 91
agent/src/main/java/cn/reghao/devops/agent/task/ImageCleanTask.java

@@ -1,91 +0,0 @@
-package cn.reghao.devops.agent.task;
-
-import cn.reghao.devops.common.docker.Docker;
-import cn.reghao.devops.common.machine.Machine;
-import cn.reghao.devops.common.msg.MessageSender;
-import cn.reghao.devops.common.msg.event.EvtTaskResult;
-import cn.reghao.jutil.jdk.event.message.EventMessage;
-import com.github.dockerjava.api.command.InspectContainerResponse;
-import com.github.dockerjava.api.model.Image;
-import lombok.extern.slf4j.Slf4j;
-
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.stream.Collectors;
-
-/**
- * 删除和运行容器依赖镜像不同 tag 的镜像
- *
- * @author reghao
- * @date 2024-07-29 16:17:47
- */
-@Slf4j
-public class ImageCleanTask implements Runnable {
-    private final String taskName;
-    private final MessageSender messageSender;
-    private final Docker docker;
-
-    public ImageCleanTask(MessageSender messageSender, Docker docker) {
-        this.taskName = this.getClass().getSimpleName();
-        this.messageSender = messageSender;
-        this.docker = docker;
-    }
-
-    @Override
-    public void run() {
-        String result = "exec ImageCleanTask";
-        log.info("{}", result);
-        EvtTaskResult evtTaskResult = new EvtTaskResult(Machine.ID, taskName, result);
-        try {
-            EventMessage evtMsg = EventMessage.evt(evtTaskResult);
-            messageSender.send("", evtMsg);
-        } catch (IOException e) {
-            e.printStackTrace();
-        }
-
-        Map<String, Image> map = docker.images().stream()
-                .collect(Collectors.toMap(Image::getId, image -> image));
-        List<InspectContainerResponse> list = docker.psAll();
-        // repo -> imageId
-        Map<String, String> repoMap = new HashMap<>();
-        for (InspectContainerResponse response : list) {
-            String imageId = response.getImageId();
-            Image image = map.get(imageId);
-            if (image != null) {
-                String[] repoTags = image.getRepoTags();
-                if (repoTags != null && repoTags.length > 1) {
-                    String repo = repoTags[0].split(":")[0];
-                    repoMap.put(repo, imageId);
-                }
-            }
-        }
-
-        map.values().forEach(image -> {
-            String imageId = image.getId();
-            String[] repoTags = image.getRepoTags();
-            if (repoTags != null && repoTags.length > 1) {
-                String repo = repoTags[0].split(":")[0];
-                String imageId0 = repoMap.get(repo);
-                if (imageId0 != null && !imageId.equals(imageId0)) {
-                    rmImage(image);
-                }
-            }
-        });
-    }
-
-    private void rmImage(Image image) {
-        try {
-            String imageId = image.getId();
-            docker.imageRm(imageId);
-            String[] tags = image.getRepoTags();
-            String result = String.format("delete image %s", tags);
-            EvtTaskResult evtTaskResult = new EvtTaskResult(Machine.ID, taskName, result);
-            EventMessage evtMsg = EventMessage.evt(evtTaskResult);
-            messageSender.send("", evtMsg);
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-    }
-}

+ 0 - 133
agent/src/main/java/cn/reghao/devops/agent/task/ProcScanTask.java

@@ -1,133 +0,0 @@
-package cn.reghao.devops.agent.task;
-
-import cn.reghao.devops.common.docker.Docker;
-import cn.reghao.devops.common.machine.Machine;
-import cn.reghao.devops.common.machine.model.SysProcess;
-import cn.reghao.devops.common.msg.MessageSender;
-import cn.reghao.devops.common.msg.event.EvtTaskResult;
-import cn.reghao.jutil.jdk.event.message.EventMessage;
-import com.github.dockerjava.api.command.InspectContainerResponse;
-import lombok.extern.slf4j.Slf4j;
-import oshi.SystemInfo;
-import oshi.software.os.InternetProtocolStats;
-import oshi.software.os.OSThread;
-import oshi.software.os.OperatingSystem;
-
-import java.io.IOException;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.util.List;
-import java.util.Map;
-import java.util.stream.Collectors;
-
-/**
- * @author reghao
- * @date 2025-12-17 14:30:36
- */
-@Slf4j
-public class ProcScanTask implements Runnable {
-    private final String taskName;
-    private final MessageSender messageSender;
-    private final Docker docker;
-    private final OperatingSystem os;
-
-    public ProcScanTask(MessageSender messageSender, Docker docker) {
-        this.taskName = this.getClass().getSimpleName();
-        this.messageSender = messageSender;
-        this.docker = docker;
-        SystemInfo si = new SystemInfo();
-        this.os = si.getOperatingSystem();
-    }
-
-    @Override
-    public void run() {
-        String result = "exec ProcScanTask";
-        log.info("{}", result);
-        EvtTaskResult evtTaskResult = new EvtTaskResult(Machine.ID, taskName, result);
-        try {
-            EventMessage evtMsg = EventMessage.evt(evtTaskResult);
-            messageSender.send("", evtMsg);
-        } catch (IOException e) {
-            e.printStackTrace();
-        }
-
-        Map<Integer, SysProcess> procMap = os.getProcesses().stream()
-                .map(osProcess -> {
-                    int pid = osProcess.getProcessID();
-                    String name = osProcess.getName();
-                    int ppid = osProcess.getParentProcessID();
-                    String state = osProcess.getState().name();
-                    List<OSThread> osThreadList = osProcess.getThreadDetails();
-                    String cmdLine = osProcess.getCommandLine();
-                    long startTime = osProcess.getStartTime();
-                    String user = osProcess.getUser();
-                    return new SysProcess(pid, name, ppid, cmdLine, startTime, user);
-                })
-                .collect(Collectors.toMap(SysProcess::getPid, sp -> sp));
-
-        String state = InternetProtocolStats.TcpState.LISTEN.name();
-        os.getInternetProtocolStats().getConnections().forEach(ipConnection -> {
-            if (!ipConnection.getState().name().equals(state)) {
-                return;
-            }
-
-            String connType = ipConnection.getType();
-            int pid = ipConnection.getowningProcessId();
-            byte[] localAddress = ipConnection.getLocalAddress();
-            String localAddressStr = byte2Str(localAddress);
-            int localPort = ipConnection.getLocalPort();
-            byte[] remoteAddress = ipConnection.getForeignAddress();
-            int remotePort = ipConnection.getForeignPort();
-            String hostPort = String.format("%s:%s", localAddressStr, localPort);
-
-            SysProcess sysProcess = procMap.get(pid);
-            if (sysProcess != null) {
-                sysProcess.getHostPorts().add(hostPort);
-            }
-        });
-
-        List<InspectContainerResponse> inspectContainerResponseList = docker.psAll();
-        for (InspectContainerResponse response : inspectContainerResponseList) {
-            Boolean running = response.getState().getRunning();
-            if (running != null && running) {
-                Integer pid = response.getState().getPid();
-                if (pid == null) {
-                    continue;
-                }
-
-                String containerId = response.getId();
-                String appId = response.getName().replace("/", "");
-                SysProcess sysProcess = procMap.get(pid);
-                if (sysProcess != null) {
-                    sysProcess.setContainerId(containerId);
-                    sysProcess.setAppId(appId);
-                } else {
-                    log.error("docker process {} not exist", pid);
-                }
-            }
-        }
-
-        List<SysProcess> listenProcessList = procMap.values().stream()
-                .filter(sysProcess -> !sysProcess.getHostPorts().isEmpty())
-                .collect(Collectors.toList());
-        evtTaskResult = new EvtTaskResult(Machine.ID, taskName, listenProcessList);
-        try {
-            EventMessage evtMsg = EventMessage.evt(evtTaskResult);
-            messageSender.send("", evtMsg);
-        } catch (IOException e) {
-            e.printStackTrace();
-        }
-    }
-
-    private String byte2Str(byte[] buffer) {
-        String address = "-1.-1.-1.-1";
-        try {
-            InetAddress inetAddress = InetAddress.getByAddress(buffer);
-            address = inetAddress.getHostAddress();
-        } catch (UnknownHostException e) {
-            e.printStackTrace();
-        }
-
-        return address;
-    }
-}

+ 205 - 0
agent/src/main/java/cn/reghao/devops/agent/ws/WebSocketClient.java

@@ -0,0 +1,205 @@
+package cn.reghao.devops.agent.ws;
+
+import cn.reghao.devops.agent.config.AgentConfig;
+import cn.reghao.devops.agent.service.AppJob;
+import cn.reghao.devops.agent.service.DeployJob;
+import cn.reghao.devops.common.msg.AgentCommand;
+import cn.reghao.devops.common.msg.AgentResponse;
+import cn.reghao.devops.common.msg.AgentResponseType;
+import cn.reghao.devops.common.msg.constant.WsClientType;
+import cn.reghao.devops.common.msg.event.EvtAppStat;
+import cn.reghao.devops.common.util.Machine;
+import cn.reghao.devops.common.msg.event.EvtAgentStart;
+import cn.reghao.devops.common.msg.event.EvtAppDeploy;
+import cn.reghao.devops.common.util.JsonUtils;
+import com.fasterxml.jackson.databind.JsonNode;
+import lombok.extern.slf4j.Slf4j;
+
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.WebSocket;
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * @author reghao
+ * @date 2026-03-10 09:11:05
+ */
+@Slf4j
+public class WebSocketClient {
+    private final String serverUri;
+    private WebSocket webSocket;
+    private final HttpClient httpClient = HttpClient.newHttpClient();
+    private int reconnectAttempts = 0;
+    // 调度器:负责发心跳 + 负责检查超时
+    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
+    // 处理接收到的任务
+    private final ExecutorService taskExecutor = Executors.newFixedThreadPool(
+            Runtime.getRuntime().availableProcessors() * 2
+    );
+
+    private final DeployJob deployJob = new DeployJob();
+    private final AppJob appJob = new AppJob();
+
+    public WebSocketClient(AgentConfig agentConfig) {
+        this.serverUri = String.format("%s/bgws/agent?type=%s&machineId=%s", agentConfig.getServerUrl(), WsClientType.AGENT.name(), Machine.ID);
+    }
+
+    public void start() {
+        connect();
+    }
+
+    public void connect() {
+        httpClient.newWebSocketBuilder()
+                .buildAsync(URI.create(serverUri), new AgentWebSocketListener())
+                .whenComplete((ws, ex) -> {
+                    if (ex != null) {
+                        reconnectAttempts++;
+                        long delay = calculateDelay(reconnectAttempts);
+                        log.error("连接失败 (第 {} 次), {} 秒后重连...", reconnectAttempts, delay);
+
+                        // 指数退避重连
+                        scheduler.schedule(this::connect, delay, TimeUnit.SECONDS);
+                    } else {
+                        // 连接成功,重置计数器
+                        this.webSocket = ws;
+                        this.reconnectAttempts = 0;
+                    }
+                });
+    }
+
+    private long calculateDelay(int attempts) {
+        // 最大等待 60 秒
+        return Math.min((long) Math.pow(2, attempts), 60);
+    }
+
+    public void stop() {
+        if (webSocket != null) {
+        }
+    }
+
+    private class AgentWebSocketListener implements WebSocket.Listener {
+        // 核心:记录最后一次收到 HEARTBEAT_ACK 的时间
+        private final AtomicLong lastAckTime = new AtomicLong(System.currentTimeMillis());
+        private final long HEARTBEAT_INTERVAL_MS = 10_000; // 10秒发一次心跳
+        private final long ACK_TIMEOUT_MS = 30_000;       // 3 个周期没收到就判定为断连
+
+        @Override
+        public void onOpen(WebSocket webSocket) {
+            // 关键操作:请求下一条消息,否则 Listener 不会收到后续数据
+            webSocket.request(1);
+            // 1. 初始化时间戳(防止刚连上就触发超时)
+            lastAckTime.set(System.currentTimeMillis());
+
+            // 异步发送注册包,不阻塞连接线程
+            taskExecutor.execute(() -> {
+                EvtAgentStart evtAgentStart = Machine.getEvtAgentStart();
+                AgentResponse<EvtAgentStart> reg = new AgentResponse<>();
+                reg.setType(AgentResponseType.REGISTER);
+                reg.setAgentId(Machine.ID);
+                reg.setData(evtAgentStart);
+
+                webSocket.sendText(JsonUtils.toJson(reg), true);
+                log.info("Agent 已注册至 Manager");
+            });
+        }
+
+        @Override
+        public CompletionStage<?> onClose(WebSocket webSocket, int statusCode, String reason) {
+            log.info("连接已关闭,准备重连...");
+            scheduler.schedule(WebSocketClient.this::connect, 5, TimeUnit.SECONDS);
+            return null;
+        }
+
+        @Override
+        public void onError(WebSocket webSocket, Throwable error) {
+            log.error("WebSocket 异常: {}", error.getMessage());
+        }
+
+        @Override
+        public CompletionStage<?> onText(WebSocket webSocket, CharSequence data, boolean last) {
+            String message = data.toString();
+            JsonNode root = JsonUtils.readTree(message);
+            // 1. 直接解析为枚举对象
+            AgentCommand cmd = AgentCommand.fromString(root.path("type").asText());
+            String taskId = root.path("taskId").asText();
+            if (cmd == AgentCommand.REGISTER_ACK) {
+                log.info("注册确认收到,开启定时心跳任务...");
+                // 只有收到 ACK,才启动心跳定时器
+                startHeartbeatScheduler();
+            } else if (cmd == AgentCommand.HEARTBEAT_ACK) {
+                // 更新最后成功交互时间
+                lastAckTime.set(System.currentTimeMillis());
+            } else {
+                // 使用虚拟线程或线程池处理任务,避免阻塞 WebSocket 读取线程
+                taskExecutor.execute(() -> {
+                    try {
+                        switch (cmd) {
+                            case DEPLOY_TASK -> {
+                                EvtAppDeploy evtAppDeploy = JsonUtils.convert(root.get("data"), EvtAppDeploy.class);
+                                deployJob.handleDeploy(webSocket, evtAppDeploy);
+                            }
+                            case APP_TASK -> {
+                                EvtAppStat evtAppStat = JsonUtils.convert(root.get("data"), EvtAppStat.class);
+                                appJob.handleApp(webSocket, evtAppStat);
+                            }
+                            case UNKNOWN -> {
+                                log.error("收到未知指令类型: {}", message);
+                            }
+                        }
+                    } catch (Exception e) {
+                        String errorMsg = e.getMessage();
+                        AgentResponse<Object> resp = new AgentResponse<>();
+                        resp.setAgentId(Machine.ID);
+                        resp.setTaskId(taskId);
+                        resp.setType(AgentResponseType.TASK_ERROR);
+                        resp.setData(errorMsg);
+                        String json = JsonUtils.toJson(resp);
+                        if (webSocket != null && !webSocket.isOutputClosed()) {
+                            webSocket.sendText(json, true).whenComplete((ws, ex) -> {
+                                if (ex != null) {
+                                    log.error("发送失败,这可能导致输出流被标记为关闭!", ex);
+                                    // 这里可以触发重连
+                                }
+                            });
+                        }
+                    }
+                });
+            }
+
+            // 立即返回,继续监听下一条消息
+            return WebSocket.Listener.super.onText(webSocket, data, last);
+        }
+
+        private void startHeartbeatScheduler() {
+            // 启动定时心跳, 每 60 秒发送一次心跳
+            scheduler.scheduleAtFixedRate(this::sendHeartbeat, 10, 60, TimeUnit.SECONDS);
+            // 启动“看门狗”检测线程:每10秒检查一次是否超时
+            //scheduler.scheduleAtFixedRate(this::checkAckTimeout, 10, 10, TimeUnit.SECONDS);
+        }
+
+        /**
+         * 看门狗检测逻辑
+         */
+        private void checkAckTimeout() {
+            long now = System.currentTimeMillis();
+            if (now - lastAckTime.get() > ACK_TIMEOUT_MS) {
+                log.error("🚨 检测到心跳 ACK 超时!Manager 可能已假死或链路中断。");
+
+                // 主动关闭当前连接,触发 Listener 的 onClose 逻辑
+                if (webSocket != null) {
+                    // 发送关闭帧并断开,触发重连流程
+                    webSocket.sendClose(WebSocket.NORMAL_CLOSURE, "Heartbeat ACK timeout");
+                }
+            }
+        }
+
+        private void sendHeartbeat() {
+            if (webSocket != null && !webSocket.isOutputClosed()) {
+                AgentResponse<String> agentResponse = AgentResponse.ok(AgentResponseType.HEARTBEAT, "", "PING");
+                String msg = JsonUtils.toJson(agentResponse);
+                webSocket.sendText(msg, true);
+            }
+        }
+    }
+}

+ 0 - 102
agent/src/main/java/cn/reghao/devops/agent/ws/WebSocketListenerImpl.java

@@ -1,102 +0,0 @@
-package cn.reghao.devops.agent.ws;
-
-import cn.reghao.devops.agent.ws.event.EventCenter;
-import cn.reghao.devops.agent.machine.MachineEvent;
-import cn.reghao.jutil.jdk.event.message.EventMessage;
-import cn.reghao.jutil.jdk.serializer.JdkSerializer;
-import lombok.extern.slf4j.Slf4j;
-import okhttp3.Response;
-import okhttp3.WebSocket;
-import okhttp3.WebSocketListener;
-import okio.ByteString;
-
-/**
- * @author reghao
- * @date 2023-02-23 09:26:50
- */
-@Slf4j
-public class WebSocketListenerImpl extends WebSocketListener {
-    private final WsClient wsClient;
-    private final EventCenter eventCenter;
-    private final MachineEvent machineEvent;
-    private final boolean logstash;
-
-    public WebSocketListenerImpl(WsClient wsClient, boolean logstash, EventCenter eventCenter, MachineEvent machineEvent) {
-        this.wsClient = wsClient;
-        this.logstash = logstash;
-        this.eventCenter = eventCenter;
-        this.machineEvent = machineEvent;
-    }
-
-    @Override
-    public void onOpen(WebSocket webSocket, Response response) {
-        log.info("WebSocket 连接成功");
-        wsClient.setConnected(true);
-        wsClient.resetRetryCount();
-
-        if (!logstash) {
-            machineEvent.agentStart();
-        }
-    }
-
-    @Override
-    public void onClosing(WebSocket webSocket, int code, String reason) {
-        log.error("WebSocket 连接正在关闭 -> {} - {}", code, reason);
-        wsClient.setConnected(false);
-
-        if (!logstash) {
-            machineEvent.pauseHeartbeat();
-        }
-
-        if (wsClient.isRetry()) {
-            reconnect();
-        }
-    }
-
-    @Override
-    public void onClosed(WebSocket webSocket, int code, String reason) {
-        log.error("WebSocket 连接关闭成功 -> {} - {}", code, reason);
-        wsClient.setConnected(false);
-    }
-
-    @Override
-    public void onFailure(WebSocket webSocket, Throwable throwable, Response response) {
-        log.info("WebSocket 异常事件: {}", throwable.getMessage());
-        wsClient.setConnected(false);
-        if (wsClient.isRetry()) {
-            reconnect();
-        }
-    }
-
-    private void reconnect() {
-        log.info("WebSocket 重连");
-        try {
-            if (wsClient.getRetryCount() > 10) {
-                log.info("WebSocket 重连超过 10 次, 休眠 1 分钟后再尝试");
-                Thread.sleep(60_000);
-                wsClient.resetRetryCount();
-            } else {
-                log.info("休眠 10s 后再尝试重连");
-                Thread.sleep(10_000);
-            }
-        } catch (InterruptedException e) {
-            e.printStackTrace();
-        }
-        wsClient.close0();
-        wsClient.retryCountIncr();
-        wsClient.connect();
-    }
-
-    @Override
-    public void onMessage(WebSocket webSocket, String text) {
-    }
-
-    @Override
-    public void onMessage(WebSocket webSocket, ByteString bytes) {
-        Object object = JdkSerializer.deserialize(bytes.toByteArray());
-        if (object instanceof EventMessage) {
-            EventMessage eventMessage = (EventMessage) object;
-            eventCenter.dispatch(eventMessage);
-        }
-    }
-}

+ 0 - 123
agent/src/main/java/cn/reghao/devops/agent/ws/WsClient.java

@@ -1,123 +0,0 @@
-package cn.reghao.devops.agent.ws;
-
-import cn.reghao.devops.agent.config.AgentConfig;
-import cn.reghao.devops.agent.service.FileReader;
-import cn.reghao.devops.agent.ws.event.EventCenter;
-import cn.reghao.devops.agent.machine.MachineEvent;
-import cn.reghao.devops.common.agent.app.iface.AppDeploy;
-import cn.reghao.devops.common.agent.app.iface.AppStat;
-import cn.reghao.devops.common.machine.Machine;
-import cn.reghao.devops.common.msg.MessageSender;
-import cn.reghao.jutil.jdk.serializer.JdkSerializer;
-import okhttp3.OkHttpClient;
-import okhttp3.Request;
-import okhttp3.WebSocket;
-import okhttp3.WebSocketListener;
-import okio.ByteString;
-
-import java.util.concurrent.TimeUnit;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-/**
- * @author reghao
- * @date 2023-02-23 09:26:50
- */
-public class WsClient implements MessageSender {
-    private final String url;
-    private WebSocket webSocket;
-    private boolean connected;
-    private final WebSocketListener webSocketListener;
-    private boolean retry;
-    private int retryCount;
-
-    public WsClient(AgentConfig agentConfig, FileReader fileReader, AppDeploy appDeploy, AppStat appStat) {
-        String protocol = agentConfig.getProtocol();
-        String host = agentConfig.getHost();
-        int port = agentConfig.getPort();
-        String app = "agent";
-        String host1 = "127.0.0.1";
-        this.url = String.format("%s://%s:%s/bgws/agent?token=%s&app=%s&host=%s", protocol, host, port, Machine.ID, app, host1);
-        boolean logstash = agentConfig.isLogstash();
-
-        EventCenter eventCenter = new EventCenter(this, appDeploy, appStat);
-        MachineEvent machineEvent = new MachineEvent(this, new Machine(), appStat);
-        this.webSocketListener = new WebSocketListenerImpl(this, logstash, eventCenter, machineEvent);
-        this.retry = true;
-        this.retryCount = 0;
-
-        if (logstash) {
-            fileReader.start(this);
-        }
-        Logger.getLogger(OkHttpClient.class.getName()).setLevel(Level.FINE);
-    }
-
-    public void setRetry(boolean retry) {
-        this.retry = retry;
-    }
-
-    public boolean isRetry() {
-        return retry;
-    }
-
-    public void retryCountIncr() {
-        this.retryCount += 1;
-    }
-
-    public void resetRetryCount() {
-        this.retryCount = 0;
-    }
-
-    public int getRetryCount() {
-        return retryCount;
-    }
-
-    @Override
-    public void connect() {
-        Request request = new Request.Builder()
-                .url(url)
-                .header("Authorization", "Bearer " + Machine.ID)
-                .build();
-
-        OkHttpClient okHttpClient = new OkHttpClient.Builder()
-                .pingInterval(10, TimeUnit.SECONDS)
-                .connectTimeout(60, TimeUnit.SECONDS)
-                .readTimeout(60, TimeUnit.SECONDS)
-                .writeTimeout(60, TimeUnit.SECONDS)
-                .build();
-
-        if (this.webSocket != null) {
-            this.webSocket.cancel();
-        }
-
-
-        this.webSocket = okHttpClient.newWebSocket(request, webSocketListener);
-    }
-
-    @Override
-    public void setConnected(boolean status) {
-        this.connected = status;
-    }
-
-    @Override
-    public boolean isConnected() {
-        return connected;
-    }
-
-    @Override
-    public void send(String dest, Object message) {
-        if (isConnected()) {
-            byte[] bytes = JdkSerializer.serialize(message);
-            webSocket.send(ByteString.of(bytes));
-        }
-    }
-
-    public void close() {
-        setRetry(false);
-        webSocket.close(1000, "Client Close Connection");
-    }
-
-    public void close0() {
-        webSocket.close(1000, "Client Close Connection");
-    }
-}

+ 0 - 60
agent/src/main/java/cn/reghao/devops/agent/ws/event/EventCenter.java

@@ -1,60 +0,0 @@
-package cn.reghao.devops.agent.ws.event;
-
-import cn.reghao.devops.agent.ws.event.handler.EvtAppDeployHandler;
-import cn.reghao.devops.agent.ws.event.handler.EvtAppStatHandler;
-import cn.reghao.devops.agent.ws.event.handler.EvtDockerHandler;
-import cn.reghao.devops.common.agent.app.iface.AppDeploy;
-import cn.reghao.devops.common.agent.app.iface.AppStat;
-import cn.reghao.devops.common.msg.MessageSender;
-import cn.reghao.devops.common.msg.event.EvtAppDeploy;
-import cn.reghao.devops.common.msg.event.EvtAppStat;
-import cn.reghao.devops.common.msg.event.EvtDockerOps;
-import cn.reghao.jutil.jdk.event.message.Event;
-import cn.reghao.jutil.jdk.event.message.EventMessage;
-import cn.reghao.jutil.jdk.event.router.EventDispatcher;
-import cn.reghao.jutil.jdk.exception.ExceptionUtil;
-import lombok.extern.slf4j.Slf4j;
-
-import java.lang.management.ManagementFactory;
-
-/**
- * @author reghao
- * @date 2023-02-23 09:18:11
- */
-@Slf4j
-public class EventCenter {
-    private final long startTime;
-    private final EventDispatcher dispatcher;
-
-    public EventCenter(MessageSender messageSender, AppDeploy appDeploy, AppStat appStat) {
-        this.startTime = ManagementFactory.getRuntimeMXBean().getStartTime();
-        this.dispatcher = new EventDispatcher();
-        initDispatcher(messageSender, appDeploy, appStat);
-    }
-
-    private void initDispatcher(MessageSender messageSender, AppDeploy appDeploy, AppStat appStat) {
-        dispatcher.register(EvtAppDeploy.class, new EvtAppDeployHandler(messageSender, appDeploy));
-        dispatcher.register(EvtAppStat.class, new EvtAppStatHandler(messageSender, appStat));
-        dispatcher.register(EvtDockerOps.class, new EvtDockerHandler(messageSender));
-    }
-
-    public void dispatch(EventMessage eventMessage) {
-        try {
-            long sendTime = eventMessage.getSendTime();
-            if (sendTime < startTime) {
-                log.info("忽略 agent 启动前 mgr 发送的事件...");
-                return;
-            }
-
-            Event event = eventMessage.getEvent();
-            dispatcher.dispatch(event);
-        } catch (Exception e) {
-            String errMsg = e.getMessage();
-            if (e instanceof NullPointerException) {
-                errMsg = ExceptionUtil.stackTrace(e);
-            }
-
-            log.error("处理消息发生异常: {}", errMsg);
-        }
-    }
-}

+ 0 - 51
agent/src/main/java/cn/reghao/devops/agent/ws/event/handler/EvtAppDeployHandler.java

@@ -1,51 +0,0 @@
-package cn.reghao.devops.agent.ws.event.handler;
-
-import cn.reghao.devops.common.agent.app.iface.AppDeploy;
-import cn.reghao.devops.common.machine.Machine;
-import cn.reghao.devops.common.msg.MessageSender;
-import cn.reghao.devops.common.msg.event.EvtAppDeploy;
-import cn.reghao.devops.common.msg.event.EvtAppStatResult;
-import cn.reghao.jutil.jdk.event.handler.Handler;
-import cn.reghao.jutil.jdk.event.message.Event;
-import cn.reghao.jutil.jdk.event.message.EventMessage;
-import cn.reghao.jutil.jdk.exception.ExceptionUtil;
-import cn.reghao.jutil.jdk.web.result.Result;
-import cn.reghao.jutil.jdk.web.result.ResultStatus;
-
-import java.io.IOException;
-
-/**
- * @author reghao
- * @date 2023-03-01 10:52:04
- */
-public class EvtAppDeployHandler extends Handler {
-    private final MessageSender messageSender;
-    private final AppDeploy appDeploy;
-
-    public EvtAppDeployHandler(MessageSender messageSender, AppDeploy appDeploy) {
-        this.messageSender = messageSender;
-        this.appDeploy = appDeploy;
-    }
-
-    @Override
-    public void handle(Event evt) {
-        EvtAppDeploy deployParam = (EvtAppDeploy) evt;
-        String appId = deployParam.getAppId();
-        EvtAppStatResult statResult;
-        try {
-            statResult = appDeploy.deploy(deployParam);
-            statResult.setResult(Result.result(ResultStatus.SUCCESS));
-        } catch (Exception e) {
-            statResult = new EvtAppStatResult(appId, Machine.ID);
-            statResult.setResult(Result.result(ResultStatus.FAIL, ExceptionUtil.errorMsg(e)));
-        }
-
-        statResult.setDeploy(true);
-        EventMessage evtMsg = EventMessage.evt(statResult);
-        try {
-            messageSender.send("", evtMsg);
-        } catch (IOException e) {
-            e.printStackTrace();
-        }
-    }
-}

+ 0 - 73
agent/src/main/java/cn/reghao/devops/agent/ws/event/handler/EvtAppStatHandler.java

@@ -1,73 +0,0 @@
-package cn.reghao.devops.agent.ws.event.handler;
-
-import cn.reghao.devops.common.agent.app.iface.AppStat;
-import cn.reghao.devops.common.machine.Machine;
-import cn.reghao.devops.common.msg.MessageSender;
-import cn.reghao.devops.common.msg.constant.AppStatOps;
-import cn.reghao.devops.common.msg.event.EvtAppStat;
-import cn.reghao.devops.common.msg.event.EvtAppStatResult;
-import cn.reghao.jutil.jdk.event.handler.Handler;
-import cn.reghao.jutil.jdk.event.message.Event;
-import cn.reghao.jutil.jdk.event.message.EventMessage;
-import cn.reghao.jutil.jdk.exception.ExceptionUtil;
-import cn.reghao.jutil.jdk.web.result.Result;
-import cn.reghao.jutil.jdk.web.result.ResultStatus;
-
-import java.io.IOException;
-
-/**
- * @author reghao
- * @date 2023-03-01 10:21:40
- */
-public class EvtAppStatHandler extends Handler {
-    private final MessageSender messageSender;
-    private final AppStat appStat;
-
-    public EvtAppStatHandler(MessageSender messageSender, AppStat appStat) {
-        this.messageSender = messageSender;
-        this.appStat = appStat;
-    }
-
-    @Override
-    public void handle(Event evt) {
-        EvtAppStat evtAppStat = (EvtAppStat) evt;
-        String appId = evtAppStat.getAppId();
-        String ops = evtAppStat.getOps();
-
-        EvtAppStatResult statResult;
-        try {
-            switch (AppStatOps.valueOf(ops)) {
-                case start:
-                    statResult = appStat.start(evtAppStat);
-                    statResult.setResult(Result.result(ResultStatus.SUCCESS));
-                    break;
-                case stop:
-                    statResult = appStat.stop(evtAppStat);
-                    statResult.setResult(Result.result(ResultStatus.SUCCESS));
-                    break;
-                case restart:
-                    statResult = appStat.restart(evtAppStat);
-                    statResult.setResult(Result.result(ResultStatus.SUCCESS));
-                    break;
-                case stat:
-                    statResult = appStat.stat(evtAppStat);
-                    statResult.setResult(Result.result(ResultStatus.SUCCESS));
-                    break;
-                default:
-                    statResult = new EvtAppStatResult(appId, Machine.ID);
-                    String msg = String.format("应用状态操作类型 %s 不存在", ops);
-                    statResult.setResult(Result.result(ResultStatus.ERROR, msg));
-            }
-        } catch (Exception e) {
-            statResult = new EvtAppStatResult(appId, Machine.ID);
-            statResult.setResult(Result.result(ResultStatus.FAIL, ExceptionUtil.errorMsg(e)));
-        }
-
-        EventMessage evtMsg = EventMessage.evt(statResult);
-        try {
-            messageSender.send(Machine.ID, evtMsg);
-        } catch (IOException e) {
-            e.printStackTrace();
-        }
-    }
-}

+ 0 - 90
agent/src/main/java/cn/reghao/devops/agent/ws/event/handler/EvtDockerHandler.java

@@ -1,90 +0,0 @@
-package cn.reghao.devops.agent.ws.event.handler;
-
-import cn.reghao.devops.agent.AgentApp;
-import cn.reghao.devops.common.docker.DockerService;
-import cn.reghao.devops.common.docker.model.DockerQuery;
-import cn.reghao.devops.common.machine.Machine;
-import cn.reghao.devops.common.msg.MessageSender;
-import cn.reghao.devops.common.msg.constant.DockerOps;
-import cn.reghao.devops.common.msg.event.EvtDockerOps;
-import cn.reghao.devops.common.msg.event.EvtDockerOpsResult;
-import cn.reghao.jutil.jdk.event.handler.Handler;
-import cn.reghao.jutil.jdk.event.message.Event;
-import cn.reghao.jutil.jdk.event.message.EventMessage;
-import lombok.extern.slf4j.Slf4j;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-import static cn.reghao.devops.common.msg.constant.DockerOps.containerRm;
-
-/**
- * @author reghao
- * @date 2025-12-16 20:03:03
- */
-@Slf4j
-public class EvtDockerHandler extends Handler {
-    private final MessageSender messageSender;
-    private final DockerService dockerService;
-
-    public EvtDockerHandler(MessageSender messageSender) {
-        this.messageSender = messageSender;
-        this.dockerService = new DockerService(AgentApp.docker);
-    }
-
-    @Override
-    public void handle(Event evt) {
-        EvtDockerOps evtDockerOps = (EvtDockerOps) evt;
-
-        String ops = evtDockerOps.getOps();
-        DockerQuery dockerQuery = evtDockerOps.getDockerQuery();
-        List<String> payload = evtDockerOps.getPayload();
-        EvtDockerOpsResult evtDockerOpsResult = new EvtDockerOpsResult(ops, Machine.ID);
-        List<Object> list = new ArrayList<>();
-        try {
-            switch (DockerOps.valueOf(ops)) {
-                case imageList -> {
-                    list.addAll(dockerService.getDockerImages(dockerQuery));
-                }
-                case imageRm -> {
-                    dockerService.rmDockerImages(evtDockerOps);
-                }
-                case imageRmUnused -> {
-                    dockerService.rmDockerImagesUnused();
-                }
-                case containerList -> list.addAll(dockerService.getDockerContainers());
-                case containerRm -> {
-                    payload.forEach(obj -> {
-                        String containerId = (String) obj;
-                        dockerService.handleDockerContainer(containerRm.name(), containerId);
-                    });
-                }
-                default -> {
-                    log.error("Unsupported Docker operation: {}", ops);
-                }
-            }
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-
-        int maxSize = 100;
-        List<Object> list0 = new ArrayList<>();
-        if (list.size() > maxSize) {
-            list0.addAll(list.subList(0, maxSize));
-        }
-
-        if (list0.isEmpty()) {
-            evtDockerOpsResult.setResultList(list);
-        } else {
-            evtDockerOpsResult.setResultList(list0);
-        }
-
-        EventMessage evtMsg = EventMessage.evt(evtDockerOpsResult);
-        try {
-            messageSender.send(Machine.ID, evtMsg);
-        } catch (IOException e) {
-            e.printStackTrace();
-        }
-    }
-}

+ 1 - 1
agent/src/main/resources/logback.xml

@@ -14,7 +14,7 @@
         </encoder>
     </appender>
 
-    <root level="DEBUG">
+    <root level="INFO">
         <appender-ref ref="FILE" />
     </root>
 </configuration>

+ 71 - 2
agent/src/test/java/AgentTest.java

@@ -1,7 +1,7 @@
 import ch.qos.logback.classic.Level;
 import ch.qos.logback.classic.Logger;
 import ch.qos.logback.classic.LoggerContext;
-import cn.reghao.devops.common.machine.model.NetworkCard;
+import cn.reghao.devops.common.util.Machine;
 import cn.reghao.jutil.jdk.converter.ByteConverter;
 import cn.reghao.jutil.jdk.converter.DateTimeConverter;
 import cn.reghao.jutil.jdk.thread.ThreadPoolWrapper;
@@ -14,6 +14,12 @@ import oshi.software.os.*;
 import oshi.util.FormatUtil;
 import oshi.util.Util;
 
+import java.io.IOException;
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.nio.file.Files;
+import java.nio.file.Paths;
 import java.util.*;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
@@ -218,7 +224,6 @@ public class AgentTest {
             long mtu = net.getMTU();
             long speed = net.getSpeed();
             String speedStr = byteConverter.convert(speed);
-            NetworkCard networkCard = new NetworkCard(iface, mac, ipv4, ipv6, mtu, speed, speedStr);
 
             System.out.format(" Name: %s (%s)%n", net.getName(), net.getDisplayName());
             System.out.format("   MAC Address: %s %n", net.getMacaddr());
@@ -325,7 +330,71 @@ public class AgentTest {
         Thread.sleep(3600_000);
     }
 
+    static String getIpv4Address() {
+        List<String> ipv4List = new ArrayList<>();
+        try {
+            Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
+            // 遍历主机的网络接口
+            while (interfaces.hasMoreElements()) {
+                NetworkInterface iface = interfaces.nextElement();
+                String ifaceName = iface.getName();
+                // 根据网络接口名前缀过滤掉 localhost 和虚拟网卡
+                if (ifaceName.startsWith("enp")
+                        || ifaceName.startsWith("wlp")
+                        || ifaceName.startsWith("eth")
+                        || ifaceName.startsWith("wlan")
+                        || ifaceName.startsWith("em")) {
+                    Enumeration<InetAddress> inetAddrs = iface.getInetAddresses();
+                    while (inetAddrs.hasMoreElements()) {
+                        InetAddress address = inetAddrs.nextElement();
+                        if (!address.isLoopbackAddress()) {
+                            if (address instanceof Inet4Address) {
+                                ipv4List.add(address.getHostAddress());
+                            }
+                        }
+                    }
+                }
+            }
+        } catch (Exception e) {
+            // ignore
+        }
+        return ipv4List.isEmpty() ? "127.0.0.1" : ipv4List.get(0);
+    }
+
+    static String getMachineId() {
+        String path = "/etc/machine-id";
+        try {
+            // 读取所有行(machine-id 通常只有一行)
+            List<String> lines = Files.readAllLines(Paths.get(path));
+            if (!lines.isEmpty()) {
+                String machineId = lines.get(0).trim();
+                return machineId;
+            }
+        } catch (IOException e) {
+            // ignore
+        }
+        return "unknown";
+    }
+
     public static void main(String[] args) throws Exception {
+        CentralProcessor centralProcessor = hal.getProcessor();
+        int cpuCore = centralProcessor.getLogicalProcessorCount();
+
+        GlobalMemory globalMemory = hal.getMemory();
+        long total = globalMemory.getTotal();
+        String mem = byteConverter.convert(total);
+
+        long bootTime = os.getSystemBootTime();
+        OperatingSystem.OSVersionInfo osVersionInfo = os.getVersionInfo();
+
+        String arch = System.getProperty("os.arch");
+        String name = System.getProperty("os.name");
+        String version = System.getProperty("os.version");
+        String ipv4Address = getIpv4Address();
+        String machineId = Machine.ID;
+        String agentVersion = "";
+
+        System.out.println();
         //setLogLevel();
         //int pid = 1483161;
         //getProcessInfo(pid);

+ 0 - 125
agent/src/test/java/RtmpFileCleaner.java

@@ -1,125 +0,0 @@
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.FileVisitResult;
-import java.nio.file.FileVisitor;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.attribute.BasicFileAttributes;
-import java.time.Duration;
-import java.time.LocalDateTime;
-import java.time.format.DateTimeFormatter;
-import java.util.Set;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.ScheduledThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
-
-/**
- * @author reghao
- * @date 2025-12-18 13:43:47
- */
-public class RtmpFileCleaner {
-    static ScheduledExecutorService scheduler = new ScheduledThreadPoolExecutor(1);
-    static Set set = Set.of("22", "23", "00", "01", "02", "03", "04", "05", "06", "07");
-
-    static void test() {
-        LocalDateTime now = LocalDateTime.now();
-        String filename = "cam204-1765896327_20251216_224527.flv";
-        String[] strArr = filename.replace(".flv", "").split("_");
-        String date = strArr[1];
-        String time = strArr[2];
-        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd HHmmss");
-        LocalDateTime localDateTime = LocalDateTime.parse(date + " " + time, formatter);
-
-        String hour = time.substring(0, 2);
-        if (set.contains(hour)) {
-        }
-
-        Duration duration = Duration.between(localDateTime, now);
-        long result = duration.getSeconds() - 3600L*12;
-        System.out.println(result);
-    }
-
-    public static void main(String[] args) {
-        String baseDir = "/opt/data/ngxflv/rtmp";
-        scheduler.scheduleAtFixedRate(new CleanerTask(baseDir), 0, 8, TimeUnit.HOURS);
-    }
-
-    static class CleanerTask implements Runnable {
-        private final String baseDir;
-        private LocalDateTime now;
-
-        public CleanerTask(String baseDir) {
-            this.baseDir = baseDir;
-        }
-
-        @Override
-        public void run() {
-            now = LocalDateTime.now();
-            try {
-                walkDir(Path.of(baseDir));
-            } catch (IOException e) {
-                e.printStackTrace();
-            }
-            System.out.printf("%s RtmpFileCleaner task completed.\n", now);
-        }
-
-        private void walkDir(Path path) throws IOException {
-            Files.walkFileTree(path, new FileVisitor<Path>() {
-                @Override
-                public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
-                    return FileVisitResult.CONTINUE;
-                }
-
-                @Override
-                public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
-                    try {
-                        process(file.toFile());
-                    } catch (Exception e) {
-                        e.printStackTrace();
-                    }
-                    return FileVisitResult.CONTINUE;
-                }
-
-                @Override
-                public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
-                    return FileVisitResult.CONTINUE;
-                }
-
-                @Override
-                public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
-                    return FileVisitResult.CONTINUE;
-                }
-            });
-        }
-
-        private void process(File file) {
-            String filename  = file.getName();
-            String[] strArr = filename.replace(".flv", "").split("_");
-            String date = strArr[1];
-            String time = strArr[2];
-            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd HHmmss");
-            LocalDateTime localDateTime = LocalDateTime.parse(date + " " + time, formatter);
-
-            LocalDateTime current = LocalDateTime.now();
-            String hour = time.substring(0, 2);
-            if (set.contains(hour)) {
-                if (file.delete()) {
-                    System.out.printf("%s Deleting file %s\n", current, file.getAbsolutePath());
-                } else {
-                    System.out.printf("%s Deleting file %s failed\n", current, file.getAbsolutePath());
-                }
-                return;
-            }
-
-            Duration duration = Duration.between(localDateTime, now);
-            long result = duration.getSeconds() - 3600L*24;
-            if (result > 0) {
-                if (file.delete()) {
-                    System.out.printf("%s Deleting file %s\n", current, file.getAbsolutePath());
-                } else {
-                    System.out.printf("%s Deleting file %s failed\n", current, file.getAbsolutePath());
-                }
-            }
-        }
-    }
-}

+ 16 - 20
agent/src/test/java/WebSocketClient.java

@@ -1,28 +1,24 @@
-import cn.reghao.devops.common.machine.Machine;
-import io.netty.bootstrap.Bootstrap;
-import io.netty.buffer.ByteBuf;
-import io.netty.buffer.Unpooled;
-import io.netty.channel.Channel;
-import io.netty.channel.ChannelInitializer;
-import io.netty.channel.ChannelPipeline;
-import io.netty.channel.EventLoopGroup;
-import io.netty.channel.nio.NioEventLoopGroup;
-import io.netty.channel.socket.SocketChannel;
-import io.netty.channel.socket.nio.NioSocketChannel;
-import io.netty.handler.codec.http.HttpClientCodec;
-import io.netty.handler.codec.http.HttpHeaders;
-import io.netty.handler.codec.http.HttpObjectAggregator;
-import io.netty.handler.codec.http.websocketx.*;
+//import io.netty.bootstrap.Bootstrap;
+//import io.netty.buffer.ByteBuf;
+//import io.netty.buffer.Unpooled;
+//import io.netty.channel.Channel;
+//import io.netty.channel.ChannelInitializer;
+//import io.netty.channel.ChannelPipeline;
+//import io.netty.channel.EventLoopGroup;
+//import io.netty.channel.nio.NioEventLoopGroup;
+//import io.netty.channel.socket.SocketChannel;
+//import io.netty.channel.socket.nio.NioSocketChannel;
+//import io.netty.handler.codec.http.HttpClientCodec;
+//import io.netty.handler.codec.http.HttpHeaders;
+//import io.netty.handler.codec.http.HttpObjectAggregator;
+//import io.netty.handler.codec.http.websocketx.*;
 
-import java.io.IOException;
-import java.net.URI;
-import java.nio.charset.StandardCharsets;
 
 /**
  * @author Stephen Mallette (http://stephen.genoprime.com)
  */
 public class WebSocketClient {
-    private final URI uri;
+    /*private final URI uri;
     private Channel ch;
     private static final EventLoopGroup group = new NioEventLoopGroup();
 
@@ -87,5 +83,5 @@ public class WebSocketClient {
         final WebSocketClient client = new WebSocketClient(url);
         client.open();
         client.eval1("哈哈哈哈");
-    }
+    }*/
 }

+ 7 - 7
agent/src/test/java/WebSocketClientHandler.java

@@ -1,15 +1,15 @@
-import io.netty.channel.*;
-import io.netty.handler.codec.http.FullHttpResponse;
-import io.netty.handler.codec.http.websocketx.*;
-import io.netty.util.CharsetUtil;
+//import io.netty.channel.*;
+//import io.netty.handler.codec.http.FullHttpResponse;
+//import io.netty.handler.codec.http.websocketx.*;
+//import io.netty.util.CharsetUtil;
 import lombok.extern.slf4j.Slf4j;
 
 /**
  * @author Stephen Mallette (http://stephen.genoprime.com)
  */
 @Slf4j
-public class WebSocketClientHandler extends SimpleChannelInboundHandler<Object> {
-    private final WebSocketClientHandshaker handshaker;
+public class WebSocketClientHandler /*extends SimpleChannelInboundHandler<Object>*/ {
+    /*private final WebSocketClientHandshaker handshaker;
     private ChannelPromise handshakeFuture;
 
     public WebSocketClientHandler(final WebSocketClientHandshaker handshaker) {
@@ -75,5 +75,5 @@ public class WebSocketClientHandler extends SimpleChannelInboundHandler<Object>
         }
 
         ctx.close();
-    }
+    }*/
 }

+ 1 - 9
bin/agent/devopsagent.json

@@ -1,12 +1,4 @@
 {
-  "protocol": "ws",
   "host": "127.0.0.1",
-  "port": 6007,
-  "logstash": true,
-  "logFiles": [
-    {
-      "domain": "reghao.cn",
-      "filePath": "/home/reghao/Downloads/access-20231107_073356-20240905_165944.log"
-    }
-  ]
+  "port": 4030
 }

+ 3 - 3
bin/mgr/devopsmgr.yml

@@ -1,5 +1,5 @@
 spring:
   datasource:
-    url: jdbc:mysql://127.0.0.1/devops_tdb?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2b8
-    username: dev
-    password: 123456
+    url: jdbc:mysql://192.168.0.220/iquizoo_devops_tdb?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2b8
+    username: azytest
+    password: Azy@123456

+ 1 - 1
bin/mgr/restart.sh

@@ -1,6 +1,6 @@
 #!/bin/bash
 
-app_name='devopsmgr.jar'
+app_name='devops-mgr.jar'
 pid=`ps -ef | grep ${app_name} | grep -v grep | awk '{print $2}'`
 if [[ -z ${pid} ]];
 then

+ 1 - 1
bin/mgr/shutdown.sh

@@ -1,6 +1,6 @@
 #!/bin/bash
 
-app_name='devopsmgr.jar'
+app_name='devops-mgr.jar'
 pid=`ps -ef | grep ${app_name} | grep -v grep | awk '{print $2}'`
 if [[ -z ${pid} ]];
 then

+ 1 - 1
bin/mgr/start.sh

@@ -1,7 +1,7 @@
 #!/bin/bash
 
 app_dir=`pwd`
-app_name='devopsmgr.jar'
+app_name='devops-mgr.jar'
 
 # 使用 mvn clean package -Dmaven.test.skip -Ptest 生成 jar
 # 运行时加载 application.yml 和 devopsmgr.yml 两个配置文件, 分别位于 classpath 和文件系统路径

+ 1 - 1
bin/mgr/start1.sh

@@ -1,5 +1,5 @@
 #!/bin/bash
 
 app_dir=`pwd`
-app_name='devopsmgr.jar'
+app_name='devops-mgr.jar'
 java -jar ${app_dir}"/"${app_name} > console.log 2>&1 &

+ 9 - 4
common/pom.xml

@@ -15,7 +15,12 @@
     <dependencies>
         <dependency>
             <groupId>com.github.docker-java</groupId>
-            <artifactId>docker-java</artifactId>
+            <artifactId>docker-java-api</artifactId>
+            <version>3.3.6</version>
+        </dependency>
+        <dependency>
+            <groupId>com.github.docker-java</groupId>
+            <artifactId>docker-java-core</artifactId>
             <version>3.3.6</version>
         </dependency>
         <dependency>
@@ -31,9 +36,9 @@
         </dependency>
 
         <dependency>
-            <groupId>com.squareup.okhttp3</groupId>
-            <artifactId>okhttp</artifactId>
-            <version>4.10.0</version>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+            <version>2.15.2</version>
         </dependency>
     </dependencies>
 </project>

+ 0 - 12
common/src/main/java/cn/reghao/devops/common/agent/app/iface/AppDeploy.java

@@ -1,12 +0,0 @@
-package cn.reghao.devops.common.agent.app.iface;
-
-import cn.reghao.devops.common.msg.event.EvtAppDeploy;
-import cn.reghao.devops.common.msg.event.EvtAppStatResult;
-
-/**
- * @author reghao
- * @date 2023-03-06 16:21:07
- */
-public interface AppDeploy {
-    EvtAppStatResult deploy(EvtAppDeploy deployParam) throws Exception;
-}

+ 0 - 20
common/src/main/java/cn/reghao/devops/common/agent/app/iface/AppStat.java

@@ -1,20 +0,0 @@
-package cn.reghao.devops.common.agent.app.iface;
-
-import cn.reghao.devops.common.msg.event.EvtAppStat;
-import cn.reghao.devops.common.msg.event.EvtAppStatResult;
-
-import java.util.List;
-
-/**
- * 应用状态管理
- *
- * @author reghao
- * @date 2023-03-06 15:22:48
- */
-public interface AppStat {
-    EvtAppStatResult start(EvtAppStat evtAppStat) throws Exception;
-    EvtAppStatResult stop(EvtAppStat evtAppStat) throws Exception;
-    EvtAppStatResult restart(EvtAppStat evtAppStat) throws Exception;
-    EvtAppStatResult stat(EvtAppStat evtAppStat) throws Exception;
-    List<EvtAppStatResult> stat() throws Exception;
-}

+ 0 - 28
common/src/main/java/cn/reghao/devops/common/agent/app/iface/impl/AppDeployImpl.java

@@ -1,28 +0,0 @@
-package cn.reghao.devops.common.agent.app.iface.impl;
-
-import cn.reghao.devops.common.agent.app.iface.AppDeploy;
-import cn.reghao.devops.common.msg.constant.PackType;
-import cn.reghao.devops.common.msg.event.EvtAppDeploy;
-import cn.reghao.devops.common.msg.event.EvtAppStatResult;
-
-/**
- * @author reghao
- * @date 2023-03-06 16:21:30
- */
-public class AppDeployImpl implements AppDeploy {
-    private final DockerApp dockerApp;
-
-    public AppDeployImpl(DockerApp dockerApp) {
-        this.dockerApp = dockerApp;
-    }
-
-    @Override
-    public EvtAppStatResult deploy(EvtAppDeploy deployParam) throws Exception {
-        String packType = deployParam.getPackType();
-        if (packType.equals(PackType.docker.name())) {
-            return dockerApp.deploy(deployParam);
-        } else {
-            throw new Exception("zip 打包没有实现");
-        }
-    }
-}

+ 0 - 73
common/src/main/java/cn/reghao/devops/common/agent/app/iface/impl/AppStatImpl.java

@@ -1,73 +0,0 @@
-package cn.reghao.devops.common.agent.app.iface.impl;
-
-import cn.reghao.devops.common.agent.app.iface.AppStat;
-import cn.reghao.devops.common.msg.constant.PackType;
-import cn.reghao.devops.common.msg.event.EvtAppStat;
-import cn.reghao.devops.common.msg.event.EvtAppStatResult;
-
-import java.util.List;
-
-/**
- * @author reghao
- * @date 2023-03-06 16:19:24
- */
-public class AppStatImpl implements AppStat {
-    private final DockerApp dockerApp;
-
-    public AppStatImpl(DockerApp dockerApp) {
-        this.dockerApp = dockerApp;
-    }
-
-    @Override
-    public EvtAppStatResult start(EvtAppStat evtAppStat) throws Exception {
-        String packType = evtAppStat.getPackType();
-        String appId = evtAppStat.getAppId();
-
-        if (packType.equals(PackType.docker.name())) {
-            return dockerApp.start(appId);
-        } else {
-            throw new Exception("zip 打包没有实现");
-        }
-    }
-
-    @Override
-    public EvtAppStatResult stop(EvtAppStat evtAppStat) throws Exception {
-        String packType = evtAppStat.getPackType();
-        String appId = evtAppStat.getAppId();
-
-        if (packType.equals(PackType.docker.name())) {
-            return dockerApp.stop(appId);
-        } else {
-            throw new Exception("zip 打包没有实现");
-        }
-    }
-
-    @Override
-    public EvtAppStatResult restart(EvtAppStat evtAppStat) throws Exception {
-        String packType = evtAppStat.getPackType();
-        String appId = evtAppStat.getAppId();
-
-        if (packType.equals(PackType.docker.name())) {
-            return dockerApp.restart(appId);
-        } else {
-            throw new Exception("zip 打包没有实现");
-        }
-    }
-
-    @Override
-    public EvtAppStatResult stat(EvtAppStat evtAppStat) throws Exception {
-        String packType = evtAppStat.getPackType();
-        String appId = evtAppStat.getAppId();
-
-        if (packType.equals(PackType.docker.name())) {
-            return dockerApp.stat(appId);
-        } else {
-            throw new Exception("zip 打包没有实现");
-        }
-    }
-
-    @Override
-    public List<EvtAppStatResult> stat() throws Exception {
-        return dockerApp.stat();
-    }
-}

+ 0 - 121
common/src/main/java/cn/reghao/devops/common/agent/app/iface/impl/DockerApp.java

@@ -1,121 +0,0 @@
-package cn.reghao.devops.common.agent.app.iface.impl;
-
-import cn.reghao.devops.common.docker.Docker;
-import cn.reghao.devops.common.docker.DockerImpl;
-import cn.reghao.devops.common.docker.model.Config;
-import cn.reghao.devops.common.docker.model.DockerAuth;
-import cn.reghao.devops.common.docker.model.HostConfig;
-import cn.reghao.devops.common.machine.Machine;
-import cn.reghao.devops.common.msg.event.EvtAppDeploy;
-import cn.reghao.devops.common.msg.event.EvtAppStatResult;
-import cn.reghao.jutil.jdk.web.result.NotAvailable;
-import cn.reghao.jutil.jdk.converter.DateTimeConverter;
-import cn.reghao.jutil.jdk.serializer.JsonConverter;
-import com.github.dockerjava.api.command.InspectContainerResponse;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.stream.Collectors;
-
-/**
- * @author reghao
- * @date 2023-03-06 15:26:22
- */
-public class DockerApp {
-    private Map<String, Docker> map = new HashMap<>();
-
-    public DockerApp(Docker docker) {
-        this.map.put("noAuth", docker);
-    }
-
-    public EvtAppStatResult deploy(EvtAppDeploy deployParam) throws Exception {
-        String appId = deployParam.getAppId();
-        String packagePath = deployParam.getPackagePath();
-        Config containerConfig = JsonConverter.jsonToObject(deployParam.getStartScript(), Config.class);
-        if (containerConfig == null) {
-            containerConfig = new Config(packagePath);
-        } else {
-            containerConfig.setImage(packagePath);
-            if (containerConfig.getHostConfig() == null) {
-                containerConfig.setHostConfig(new HostConfig());
-            }
-        }
-
-        Docker docker;
-        DockerAuth dockerAuth = deployParam.getDockerAuth();
-        if (dockerAuth != null) {
-            String registryUrl = dockerAuth.getRegistryUrl();
-            docker = map.get(registryUrl);
-            if (docker == null) {
-                // TODO DockerAuth 修改后 map 中的 Docker 实例无效
-                docker = new DockerImpl(dockerAuth);
-                map.put(registryUrl, docker);
-            }
-        } else {
-            docker = map.get("noAuth");
-        }
-        docker.pull(packagePath);
-        InspectContainerResponse containerInfo = docker.createAndRun(appId, containerConfig);
-        return getStat(appId, containerInfo);
-    }
-
-    public EvtAppStatResult start(String appId) throws Exception {
-        Docker docker = map.get("noAuth");
-        InspectContainerResponse containerInfo = docker.start(appId);
-        return getStat(appId, containerInfo);
-    }
-
-    public EvtAppStatResult stop(String appId) throws Exception {
-        Docker docker = map.get("noAuth");
-        InspectContainerResponse containerInfo = docker.stop(appId);
-        return getStat(appId, containerInfo);
-    }
-
-    public EvtAppStatResult restart(String appId) throws Exception {
-        Docker docker = map.get("noAuth");
-        InspectContainerResponse containerInfo = docker.restart(appId);
-        return getStat(appId, containerInfo);
-    }
-
-    public EvtAppStatResult stat(String appId) throws Exception {
-        Docker docker = map.get("noAuth");
-        InspectContainerResponse containerInfo = docker.inspectContainer(appId);
-        return getStat(appId, containerInfo);
-    }
-
-    public List<EvtAppStatResult> stat() throws Exception {
-        Docker docker = map.get("noAuth");
-        List<InspectContainerResponse> list = docker.psAll();
-        return list.stream().map(response -> {
-            String containerName = response.getName();
-            String appId = containerName.replace("/", "");
-            return getStat(appId, response);
-        }).collect(Collectors.toList());
-    }
-
-    private EvtAppStatResult getStat(String appId, InspectContainerResponse containerInfo) {
-        EvtAppStatResult appStatResult = new EvtAppStatResult(appId, Machine.ID);
-        String image = containerInfo.getConfig().getImage();
-        if (image != null && image.contains(":")) {
-            appStatResult.setCommitId(image.split(":")[1]);
-        } else {
-            appStatResult.setCommitId(NotAvailable.na.getDesc());
-        }
-
-        InspectContainerResponse.ContainerState state = containerInfo.getState();
-        if (state == null || Boolean.FALSE.equals(state.getRunning())) {
-            //appStatResult.setStatus(NodeStatus.Offline.name());
-            appStatResult.setStartTime(null);
-            appStatResult.setPid(-1);
-        } else {
-            String startedAt = state.getStartedAt();
-            //appStatResult.setStatus(NodeStatus.Online.name());
-            appStatResult.setRunning(true);
-            appStatResult.setStartTime(DateTimeConverter.localDateTime(startedAt));
-            appStatResult.setPid(state.getPidLong().intValue());
-        }
-
-        return appStatResult;
-    }
-}

+ 8 - 3
common/src/main/java/cn/reghao/devops/common/docker/Docker.java

@@ -1,10 +1,12 @@
 package cn.reghao.devops.common.docker;
 
-import cn.reghao.devops.common.docker.model.Config;
+import cn.reghao.devops.common.docker.model.DockerAuth;
+import cn.reghao.devops.common.docker.model.DockerContainerConfig;
 import com.github.dockerjava.api.command.InspectContainerResponse;
 import com.github.dockerjava.api.model.Image;
 import com.github.dockerjava.api.model.Version;
 
+import java.io.IOException;
 import java.util.List;
 
 /**
@@ -16,12 +18,14 @@ import java.util.List;
 public interface Docker {
     void auth();
     Version version();
+    void ping();
     void build(String repoTag, String compileHome, String dockerfileContent) throws Exception;
     void push(String image) throws Exception;
     void pull(String repoTag) throws Exception;
+    void pull(String repoTag, DockerAuth dockerAuth) throws Exception;
     void stopAndDelete(String containerName) throws Exception;
-    InspectContainerResponse createAndRun(String containerName, Config containerConfig) throws Exception;
-    void runAndRm(Config containerConfig) throws Exception;
+    InspectContainerResponse createAndRun(String containerName, DockerContainerConfig containerConfig) throws Exception;
+    void runAndRm(DockerContainerConfig containerConfig) throws Exception;
     InspectContainerResponse start(String containerName);
     InspectContainerResponse stop(String containerName);
     InspectContainerResponse restart(String containerName);
@@ -31,4 +35,5 @@ public interface Docker {
     void rm(String containerId);
     List<InspectContainerResponse> psAll();
     InspectContainerResponse ps(String containerName);
+    void close() throws IOException;
 }

+ 48 - 23
common/src/main/java/cn/reghao/devops/common/docker/DockerImpl.java

@@ -1,6 +1,7 @@
 package cn.reghao.devops.common.docker;
 
 import cn.reghao.devops.common.docker.model.DockerAuth;
+import cn.reghao.devops.common.docker.model.DockerContainerConfig;
 import cn.reghao.jutil.jdk.exception.ExceptionUtil;
 import cn.reghao.jutil.jdk.io.TextFile;
 import com.github.dockerjava.api.DockerClient;
@@ -81,6 +82,11 @@ public class DockerImpl implements Docker {
         return dockerClient.versionCmd().exec();
     }
 
+    @Override
+    public void ping() {
+        dockerClient.pingCmd().exec();
+    }
+
     @Override
     public void build(String repoTag, String compileHome, String dockerfileContent) throws Exception {
         File dockerfile = new File(compileHome + "/Dockerfile.tmp");
@@ -155,6 +161,31 @@ public class DockerImpl implements Docker {
         }).onComplete();*/
     }
 
+    private AuthConfig getAuthConfig(DockerAuth dockerAuth) {
+        return new AuthConfig()
+                .withUsername(dockerAuth.getUsername())
+                .withPassword(dockerAuth.getPassword())
+                .withRegistryAddress(dockerAuth.getRegistryUrl());
+    }
+
+    public void pull(String image, DockerAuth dockerAuth) throws Exception {
+        try {
+            if (dockerAuth != null) {
+                AuthConfig authConfig = getAuthConfig(dockerAuth);
+                dockerClient.pullImageCmd(image)
+                        .withAuthConfig(authConfig)
+                        .exec(new PullImageResultCallback())
+                        .awaitCompletion();
+            } else {
+                dockerClient.pullImageCmd(image)
+                        .exec(new PullImageResultCallback())
+                        .awaitCompletion();
+            }
+        } catch (InterruptedException e) {
+            throw new Exception(ExceptionUtil.errorMsg(e));
+        }
+    }
+
     private String getContainerIdByName(String containerName) {
         List<Container> list = dockerClient.listContainersCmd()
                 .withShowAll(true)
@@ -186,24 +217,26 @@ public class DockerImpl implements Docker {
     }
 
     @Override
-    public InspectContainerResponse createAndRun(String containerName, cn.reghao.devops.common.docker.model.Config containerConfig) throws Exception {
-        stopAndDelete(containerName);
-
+    public InspectContainerResponse createAndRun(String containerName, DockerContainerConfig containerConfig) throws Exception {
+        //stopAndDelete(containerName);
         String image = containerConfig.getImage();
         CreateContainerCmd createContainerCmd = dockerClient.createContainerCmd(image)
                 .withName(containerName);
 
+
+
+        // 环境变量
         List<String> env = containerConfig.getEnv();
         if (env != null) {
             createContainerCmd.withEnv(env);
         }
 
-        RestartPolicy restartPolicy = RestartPolicy.onFailureRestart(3);
         HostConfig hostConfig = HostConfig.newHostConfig()
-                // --init 参数
-                //.withInit(true)
+                .withCpuQuota(100000L)                 // 限制使用 1.0 核 (周期默认为 100000)
+                .withMemory(1024 * 1024 * 1024L)      // 限制 1GB 内存
+                .withMemoryReservation(512 * 1024 * 1024L) // 保证 512MB 内存
                 .withNetworkMode("host")
-                .withRestartPolicy(restartPolicy);
+                .withRestartPolicy(RestartPolicy.alwaysRestart());
         if (containerConfig.getVolumes() != null) {
             Map<String, String> map = containerConfig.getVolumes().getMap();
             List<Bind> list = new ArrayList<>();
@@ -218,31 +251,18 @@ public class DockerImpl implements Docker {
         }
 
         if (containerConfig.getHostConfig().getInit() != null) {
+            // --init 参数
             hostConfig.withInit(containerConfig.getHostConfig().getInit());
         }
-
         createContainerCmd.withHostConfig(hostConfig);
+
         CreateContainerResponse response = createContainerCmd.exec();
         String containerId = response.getId();
         dockerClient.startContainerCmd(containerId).exec();
         return dockerClient.inspectContainerCmd(containerId).exec();
     }
 
-    public void createAndRun() {
-        String image = "";
-        CreateContainerResponse container = dockerClient.createContainerCmd(image)
-                .withHostConfig(HostConfig.newHostConfig()
-                        .withMemory(1024 * 1024 * 1024L)      // 限制 1GB 内存
-                        .withMemoryReservation(512 * 1024 * 1024L) // 保证 512MB 内存
-                        .withCpuQuota(50000L)                 // 限制使用 0.5 核 (周期默认为 100000)
-                        .withBlkioWeight(500)                 // 设置磁盘 IO 权重
-                        .withNetworkMode("host")
-                        .withRestartPolicy(RestartPolicy.noRestart())
-                )
-                .exec();
-    }
-
-    public void runAndRm(cn.reghao.devops.common.docker.model.Config containerConfig) throws Exception {
+    public void runAndRm(DockerContainerConfig containerConfig) throws Exception {
         String image = containerConfig.getImage();
         CreateContainerCmd createContainerCmd = dockerClient.createContainerCmd(image).withCmd("rm");
 
@@ -449,4 +469,9 @@ public class DockerImpl implements Docker {
             throwable.printStackTrace();
         }
     }
+
+    @Override
+    public void close() throws IOException {
+        dockerClient.close();
+    }
 }

+ 35 - 0
common/src/main/java/cn/reghao/devops/common/docker/DockerManager.java

@@ -0,0 +1,35 @@
+package cn.reghao.devops.common.docker;
+
+import java.io.IOException;
+
+/**
+ * @author reghao
+ * @date 2026-03-10 09:15:05
+ */
+public class DockerManager {
+    private static volatile Docker docker;
+
+    // 私有构造,初始化 DockerClient
+    private DockerManager() {}
+
+
+    public static Docker getDocker() {
+        if (docker == null) {
+            synchronized (DockerManager.class) {
+                if (docker == null) {
+                    docker = new DockerImpl();
+                }
+            }
+        }
+        return docker;
+    }
+
+    /**
+     * 优雅关闭资源
+     */
+    public static void close() throws IOException {
+        if (docker != null) {
+            docker.close();
+        }
+    }
+}

+ 0 - 130
common/src/main/java/cn/reghao/devops/common/docker/DockerService.java

@@ -1,130 +0,0 @@
-package cn.reghao.devops.common.docker;
-
-import cn.reghao.devops.common.docker.model.DockerContainer;
-import cn.reghao.devops.common.docker.model.DockerImage;
-import cn.reghao.devops.common.docker.model.DockerQuery;
-import cn.reghao.devops.common.msg.event.EvtDockerOps;
-import com.github.dockerjava.api.command.InspectContainerResponse;
-import com.github.dockerjava.api.model.Image;
-import lombok.extern.slf4j.Slf4j;
-
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.stream.Collectors;
-
-import static cn.reghao.devops.common.msg.constant.DockerOps.containerRm;
-
-/**
- * @author reghao
- * @date 2025-12-09 11:01:17
- */
-@Slf4j
-public class DockerService {
-    private final Docker docker;
-
-    public DockerService(Docker docker) {
-        this.docker = docker;
-    }
-
-    public List<DockerImage> getDockerImages(DockerQuery dockerQuery) {
-        String keyword = dockerQuery.getKeyword();
-        int type = dockerQuery.getType();
-        // imageId -> container
-        Map<String, List<InspectContainerResponse>> containerMap = docker.psAll().stream()
-                .collect(Collectors.groupingBy(InspectContainerResponse::getImageId));
-        return docker.images().stream()
-                .map(image -> {
-                    String repoTag = "<none>:<none>";
-                    if (image.getRepoTags() != null && image.getRepoTags().length > 0) {
-                        repoTag = image.getRepoTags()[0];
-                    }
-
-                    boolean matched = false;
-                    if (keyword != null && !keyword.isBlank()) {
-                        if (type == 1) {
-                            // 关键字匹配
-                            matched = repoTag.contains(keyword);
-                        } else if (type == 2) {
-                            // 前缀匹配
-                            matched = repoTag.startsWith(keyword);
-                        }
-
-                        if (!matched) {
-                            return null;
-                        }
-                    }
-
-                    int totalContainers = 0;
-                    String imageId = image.getId();
-                    List<InspectContainerResponse> list = containerMap.get(imageId);
-                    if (list != null) {
-                        totalContainers = list.size();
-                    }
-                    return new DockerImage(image, repoTag, totalContainers);
-                })
-                .filter(Objects::nonNull)
-                .collect(Collectors.toList());
-    }
-
-    public void rmDockerImagesUnused() {
-        Map<String, Image> map = docker.images().stream()
-                .collect(Collectors.toMap(Image::getId, image -> image));
-        List<InspectContainerResponse> list = docker.psAll();
-        for (InspectContainerResponse response : list) {
-            String imageId = response.getImageId();
-            map.remove(imageId);
-        }
-
-        for (Image image : map.values()) {
-            try {
-                docker.imageRm(image.getId());
-            } catch (Exception e) {
-                e.printStackTrace();
-            }
-        }
-    }
-
-    public void rmDockerImages(EvtDockerOps evtDockerOps) {
-        List<String> imageIds = evtDockerOps.getPayload();
-        imageIds.forEach(docker::imageRm);
-    }
-
-    public List<DockerContainer> getDockerContainers() {
-        Map<String, Image> imageMap = docker.images().stream()
-                .collect(Collectors.toMap(Image::getId, v -> v));
-        return docker.psAll().stream().map(inspectContainerResponse -> {
-            String imageId = inspectContainerResponse.getImageId();
-            Image image = imageMap.get(imageId);
-            if (image == null) {
-                log.error("container {}'s imageId {} not found" , inspectContainerResponse.getId(), imageId);
-                return null;
-            }
-            String repoTag = "<none>:<none>";
-            if (image.getRepoTags() != null && image.getRepoTags().length > 0) {
-                repoTag = image.getRepoTags()[0];
-            }
-            return new DockerContainer(inspectContainerResponse, repoTag);
-        }).filter(Objects::nonNull).collect(Collectors.toList());
-    }
-
-    public void handleDockerContainer(String ops, String containerId) {
-        if (!containerRm.name().equals(ops)) {
-            return;
-        }
-
-        // containerId -> InspectContainerResponse
-        Map<String, InspectContainerResponse> containerMap = docker.psAll().stream()
-                .collect(Collectors.groupingBy(
-                        InspectContainerResponse::getId,
-                        Collectors.collectingAndThen(Collectors.toList(), value -> value.get(0))
-                ));
-
-        InspectContainerResponse response = containerMap.get(containerId);
-        if (response == null) {
-            log.error("containerId {} not exist", containerId);
-            return;
-        }
-        docker.rm(containerId);
-    }
-}

+ 0 - 32
common/src/main/java/cn/reghao/devops/common/docker/model/DockerContainer.java

@@ -1,32 +0,0 @@
-package cn.reghao.devops.common.docker.model;
-
-import cn.reghao.jutil.jdk.converter.DateTimeConverter;
-import com.github.dockerjava.api.command.InspectContainerResponse;
-import lombok.Getter;
-
-import java.io.Serializable;
-
-/**
- * @author reghao
- * @date 2025-12-09 11:01:46
- */
-@Getter
-public class DockerContainer implements Serializable {
-    private static final long serialVersionUID = 1L;
-
-    private String containerId;
-    private String name;
-    private String createdAt;
-    private String status;
-    private String imageId;
-    private String repoTag;
-
-    public DockerContainer(InspectContainerResponse inspectContainerResponse,String repoTag) {
-        this.containerId = inspectContainerResponse.getId();
-        this.name = inspectContainerResponse.getName();
-        this.imageId = inspectContainerResponse.getImageId();
-        this.createdAt = DateTimeConverter.format(inspectContainerResponse.getCreated());
-        this.status = inspectContainerResponse.getState().getStatus();
-        this.repoTag = repoTag;
-    }
-}

+ 53 - 3
common/src/main/java/cn/reghao/devops/common/docker/model/Config.java → common/src/main/java/cn/reghao/devops/common/docker/model/DockerContainerConfig.java

@@ -1,9 +1,12 @@
 package cn.reghao.devops.common.docker.model;
 
 import com.google.gson.annotations.SerializedName;
+import lombok.AllArgsConstructor;
 import lombok.Data;
 
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 /**
  * docker 容器创建时的配置
@@ -12,7 +15,7 @@ import java.util.List;
  * @date 2020-01-14 23:11:21
  */
 @Data
-public class Config implements Cloneable {
+public class DockerContainerConfig implements Cloneable {
     @SerializedName("AttachStderr") private boolean attachStderr;
     @SerializedName("AttachStdin") private boolean attachStdin;
     @SerializedName("AttachStdout") private boolean attachStdout;
@@ -39,11 +42,11 @@ public class Config implements Cloneable {
     @SerializedName("HostConfig") private HostConfig hostConfig;
     @SerializedName("NetworkingConfig") private NetworkingConfig networkingConfig;
 
-    public Config() {
+    public DockerContainerConfig() {
         this.hostConfig = new HostConfig();
     }
 
-    public Config(String image) {
+    public DockerContainerConfig(String image) {
         this.image = image;
         this.hostConfig = new HostConfig();
     }
@@ -52,4 +55,51 @@ public class Config implements Cloneable {
     public Object clone() throws CloneNotSupportedException {
         return super.clone();
     }
+
+    @Data
+    public static class HostConfig {
+        @SerializedName("NetworkMode") private String networkMode;
+        @SerializedName("RestartPolicy") private RestartPolicy restartPolicy;
+        @SerializedName("Init") private Boolean init;
+
+        public HostConfig() {
+            this.networkMode = "host";
+            this.restartPolicy = new RestartPolicy();
+        }
+    }
+
+    @Data
+    public static class Volumes {
+        private Map<String, String> map = new HashMap<>();
+    }
+
+    @AllArgsConstructor
+    @Data
+    public static class RestartPolicy {
+        @SerializedName("Name") private String name;
+        @SerializedName("MaximumRetryCount") private int maximumRetryCount;
+
+        public RestartPolicy() {
+            this.name = "on-failure";
+            this.maximumRetryCount = 3;
+        }
+    }
+
+    @Data
+    public static class ExposedPorts {
+    }
+
+    @Data
+    public static class Healthcheck {
+        @SerializedName("Test") private List<String> test;
+    }
+
+    @Data
+    public static class Labels {
+        private Map<String, String> map;
+    }
+
+    @Data
+    public static class NetworkingConfig {
+    }
 }

+ 0 - 29
common/src/main/java/cn/reghao/devops/common/docker/model/DockerImage.java

@@ -1,29 +0,0 @@
-package cn.reghao.devops.common.docker.model;
-
-import cn.reghao.jutil.jdk.converter.DateTimeConverter;
-import com.github.dockerjava.api.model.Image;
-import lombok.Getter;
-
-import java.io.Serializable;
-import java.util.List;
-
-/**
- * @author reghao
- * @date 2025-12-09 11:01:37
- */
-@Getter
-public class DockerImage implements Serializable {
-    private static final long serialVersionUID = 1L;
-
-    private String imageId;
-    private String repoTag;
-    private String createdAt;
-    private int totalContainers;
-
-    public DockerImage(Image image, String repoTag, int totalContainers) {
-        this.imageId = image.getId();
-        this.repoTag = repoTag;
-        this.createdAt = DateTimeConverter.format(image.getCreated()*1000);
-        this.totalContainers = totalContainers;
-    }
-}

+ 0 - 11
common/src/main/java/cn/reghao/devops/common/docker/model/ExposedPorts.java

@@ -1,11 +0,0 @@
-package cn.reghao.devops.common.docker.model;
-
-import lombok.Data;
-
-/**
- * @author reghao
- * @date 2021-02-10 01:30:17
- */
-@Data
-public class ExposedPorts {
-}

+ 0 - 15
common/src/main/java/cn/reghao/devops/common/docker/model/Healthcheck.java

@@ -1,15 +0,0 @@
-package cn.reghao.devops.common.docker.model;
-
-import com.google.gson.annotations.SerializedName;
-import lombok.Data;
-
-import java.util.List;
-
-/**
- * @author reghao
- * @date 2021-02-25 10:02:59
- */
-@Data
-public class Healthcheck {
-    @SerializedName("Test") private List<String> test;
-}

+ 0 - 24
common/src/main/java/cn/reghao/devops/common/docker/model/HostConfig.java

@@ -1,24 +0,0 @@
-package cn.reghao.devops.common.docker.model;
-
-import com.google.gson.annotations.SerializedName;
-import lombok.AllArgsConstructor;
-import lombok.Data;
-
-/**
- * TODO 部分
- *
- * @author reghao
- * @date 2020-01-14 23:16:52
- */
-@AllArgsConstructor
-@Data
-public class HostConfig {
-    @SerializedName("NetworkMode") private String networkMode;
-    @SerializedName("RestartPolicy") private RestartPolicy restartPolicy;
-    @SerializedName("Init") private Boolean init;
-
-    public HostConfig() {
-        this.networkMode = "host";
-        this.restartPolicy = new RestartPolicy();
-    }
-}

+ 0 - 14
common/src/main/java/cn/reghao/devops/common/docker/model/Labels.java

@@ -1,14 +0,0 @@
-package cn.reghao.devops.common.docker.model;
-
-import lombok.Data;
-
-import java.util.Map;
-
-/**
- * @author reghao
- * @date 2021-02-10 01:28:38
- */
-@Data
-public class Labels {
-    private Map<String, String> map;
-}

+ 0 - 11
common/src/main/java/cn/reghao/devops/common/docker/model/NetworkingConfig.java

@@ -1,11 +0,0 @@
-package cn.reghao.devops.common.docker.model;
-
-import lombok.Data;
-
-/**
- * @author reghao
- * @date 2021-02-10 01:33:05
- */
-@Data
-public class NetworkingConfig {
-}

+ 0 - 21
common/src/main/java/cn/reghao/devops/common/docker/model/RestartPolicy.java

@@ -1,21 +0,0 @@
-package cn.reghao.devops.common.docker.model;
-
-import com.google.gson.annotations.SerializedName;
-import lombok.AllArgsConstructor;
-import lombok.Data;
-
-/**
- * @author reghao
- * @date 2020-05-19 15:00:50
- */
-@AllArgsConstructor
-@Data
-public class RestartPolicy {
-    @SerializedName("Name") private String name;
-    @SerializedName("MaximumRetryCount") private int maximumRetryCount;
-
-    public RestartPolicy() {
-        this.name = "on-failure";
-        this.maximumRetryCount = 3;
-    }
-}

+ 0 - 15
common/src/main/java/cn/reghao/devops/common/docker/model/Volumes.java

@@ -1,15 +0,0 @@
-package cn.reghao.devops.common.docker.model;
-
-import lombok.Data;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * @author reghao
- * @date 2021-02-10 01:29:10
- */
-@Data
-public class Volumes {
-    private Map<String, String> map = new HashMap<>();
-}

+ 0 - 39
common/src/main/java/cn/reghao/devops/common/machine/CPU.java

@@ -1,39 +0,0 @@
-package cn.reghao.devops.common.machine;
-
-import cn.reghao.devops.common.machine.model.CPUInfo;
-import cn.reghao.jutil.jdk.machine.data.MachineData;
-import cn.reghao.jutil.jdk.machine.data.detail.CpuDetail;
-import cn.reghao.jutil.jdk.machine.data.stat.CpuStat;
-import oshi.SystemInfo;
-import oshi.hardware.CentralProcessor;
-
-/**
- * @author reghao
- * @date 2019-10-25 13:12:36
- */
-public class CPU implements MachineData<CpuDetail, CpuStat> {
-    private final CentralProcessor processor;
-
-    public CPU(SystemInfo si) {
-        this.processor = si.getHardware().getProcessor();
-    }
-
-    @Override
-    public CpuDetail detail() {
-        CentralProcessor.ProcessorIdentifier processorIdentifier = processor.getProcessorIdentifier();
-        String vendor = processorIdentifier.getVendor();
-        String name = processorIdentifier.getName();
-        int physicalCore = processor.getPhysicalProcessorCount();
-        int logicalCore = processor.getLogicalProcessorCount();
-        return new CpuDetail(vendor, name, physicalCore, logicalCore);
-    }
-
-    @Override
-    public CpuStat stat() {
-        return null;
-    }
-
-    public CPUInfo getCPUInfo() {
-        return null;
-    }
-}

+ 0 - 158
common/src/main/java/cn/reghao/devops/common/machine/Disk.java

@@ -1,158 +0,0 @@
-package cn.reghao.devops.common.machine;
-
-import cn.reghao.devops.common.machine.model.DiskInfo;
-import cn.reghao.devops.common.machine.model.DiskPartition;
-import cn.reghao.jutil.jdk.converter.ByteConverter;
-import cn.reghao.jutil.jdk.machine.data.MachineData;
-import cn.reghao.jutil.jdk.machine.data.detail.DiskDetail;
-import cn.reghao.jutil.jdk.machine.data.stat.DiskStat;
-import cn.reghao.jutil.jdk.math.Calculator;
-import oshi.SystemInfo;
-import oshi.hardware.HardwareAbstractionLayer;
-import oshi.software.os.OperatingSystem;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-import java.util.stream.Collectors;
-
-/**
- * @author reghao
- * @date 2019-10-25 13:21:25
- */
-public class Disk implements MachineData<List<DiskDetail>, List<DiskStat>> {
-    private final HardwareAbstractionLayer hal;
-    private final OperatingSystem os;
-    private final ByteConverter byteConverter;
-
-    public Disk(SystemInfo si, ByteConverter byteConverter) {
-        this.hal = si.getHardware();
-        this.os = si.getOperatingSystem();
-        this.byteConverter = byteConverter;
-    }
-
-    @Override
-    public List<DiskDetail> detail() {
-        return os.getFileSystem().getFileStores().stream()
-                .filter(osFileStore -> osFileStore.getVolume().startsWith("/dev/"))
-                .map(osFileStore -> {
-                    String vol = osFileStore.getVolume();
-                    String mount = osFileStore.getMount();
-                    String fsType = osFileStore.getType();
-
-                    long total = osFileStore.getTotalSpace();
-                    // non-root 用户可用的磁盘空间
-                    long userAvail = osFileStore.getUsableSpace();
-                    // root 用户可用的磁盘空间
-                    long rootAvail = osFileStore.getFreeSpace();
-
-                    long inodeTotal = osFileStore.getTotalInodes();
-                    long inodeFree = osFileStore.getFreeInodes();
-
-                    return new DiskDetail(vol, mount, fsType, total, userAvail, total-userAvail, inodeTotal, inodeFree);
-                })
-                .collect(Collectors.toList());
-    }
-
-    @Override
-    public List<DiskStat> stat() {
-        return null;
-    }
-
-    /**
-     * 根据一个路径确定它所在的分区
-     *
-     * @param
-     * @return
-     * @date 2021-02-06 下午11:25
-     */
-    public DiskDetail diskDetail(String path) {
-        List<DiskDetail> diskInfos = detail();
-        // 根据挂载路径的长度降序
-        diskInfos.sort((o1, o2) -> o2.getMountedOn().length() - o1.getMountedOn().length());
-
-        List<DiskDetail> candidates = new ArrayList<>();
-        for (DiskDetail diskInfo : diskInfos) {
-            String mountedOn = diskInfo.getMountedOn();
-            String[] array = path.split(mountedOn);
-            if (array.length == 0) {
-                return diskInfo;
-            } else if (array.length == 2) {
-                if (array[1].startsWith("/") || "/".equals(mountedOn)) {
-                    candidates.add(diskInfo);
-                }
-            } else if (array.length > 2) {
-                candidates.add(diskInfo);
-            }
-        }
-
-        if (!candidates.isEmpty()) {
-            // TODO 如果 candidates 大于 0 则判断准确的那个
-            return candidates.get(0);
-        } else {
-            return null;
-        }
-    }
-
-    public List<DiskInfo> getDisks() {
-        List<DiskInfo> diskInfoList = new ArrayList<>();
-        hal.getDiskStores().forEach(hwDiskStore -> {
-            String model = hwDiskStore.getModel();
-            String name = hwDiskStore.getName();
-            long size = hwDiskStore.getSize();
-            String sizeStr = byteConverter.convert(size);
-            DiskInfo diskInfo = new DiskInfo(model, name, size, sizeStr);
-            hwDiskStore.getPartitions().forEach(hwPartition -> {
-                String uuid = hwPartition.getUuid();
-                String id = hwPartition.getIdentification();
-                String mountedOn = hwPartition.getMountPoint();
-                String fsType = hwPartition.getType();
-                long pSize = hwPartition.getSize();
-
-                DiskPartition diskPartition = new DiskPartition(name, pSize, uuid, id, mountedOn, fsType);
-                diskPartition.setTotalSpace(pSize);
-                diskInfo.getDiskPartitionList().add(diskPartition);
-            });
-
-            diskInfoList.add(diskInfo);
-        });
-
-        Map<String, DiskPartition> map = diskInfoList.stream()
-                .map(DiskInfo::getDiskPartitionList)
-                .flatMap(Collection::stream)
-                .collect(Collectors.groupingBy(DiskPartition::getUuid,
-                        Collectors.collectingAndThen(Collectors.toList(), value -> value.get(0))));
-        os.getFileSystem().getFileStores().forEach(osFileStore -> {
-            String uuid = osFileStore.getUUID();
-            String vol = osFileStore.getVolume();
-            long totalSpace = osFileStore.getTotalSpace();
-            String totalSpaceStr = byteConverter.convert(totalSpace);
-            long freeSpace = osFileStore.getFreeSpace();
-            String freeSpaceStr = byteConverter.convert(freeSpace);
-            long usableSpace = osFileStore.getUsableSpace();
-            String usableSpaceStr = byteConverter.convert(usableSpace);
-            long totalInode = osFileStore.getTotalInodes();
-            long freeInode = osFileStore.getFreeInodes();
-
-            DiskPartition diskPartition = map.get(uuid);
-            if (diskPartition != null) {
-                diskPartition.setTotalSpace(totalSpace);
-                diskPartition.setTotalSpaceStr(totalSpaceStr);
-                diskPartition.setFreeSpace(freeSpace);
-                diskPartition.setFreeSpaceStr(freeSpaceStr);
-                diskPartition.setUsableSpace(usableSpace);
-                diskPartition.setUsableSpaceStr(usableSpaceStr);
-                double freeSpacePercent = Calculator.divide(freeSpace, totalSpace)*100;
-                diskPartition.setFreeSpacePercent(freeSpacePercent);
-
-                diskPartition.setTotalInode(totalInode);
-                diskPartition.setFreeInode(freeInode);
-                double freeInodePercent = Calculator.divide(freeInode, totalInode)*100;
-                diskPartition.setFreeInodePercent(freeInodePercent);
-            }
-        });
-
-        return diskInfoList;
-    }
-}

+ 0 - 76
common/src/main/java/cn/reghao/devops/common/machine/Machine.java

@@ -1,76 +0,0 @@
-package cn.reghao.devops.common.machine;
-
-import cn.reghao.devops.common.msg.event.EvtAgentHeartbeat;
-import cn.reghao.devops.common.msg.event.EvtAgentStart;
-import cn.reghao.jutil.jdk.converter.ByteConverter;
-import cn.reghao.jutil.jdk.machine.data.detail.*;
-import cn.reghao.jutil.jdk.machine.id.MachineId;
-import cn.reghao.jutil.jdk.machine.id.MachineIdLinux;
-import cn.reghao.jutil.jdk.web.result.AppVersion;
-import oshi.SystemInfo;
-import oshi.hardware.HardwareAbstractionLayer;
-import oshi.software.os.OperatingSystem;
-
-import java.util.List;
-import java.util.stream.Collectors;
-
-/**
- * @author reghao
- * @date 2020-10-22 15:47:58
- */
-public class Machine {
-    public final static String ID;
-    public final static String IPV4;
-    static {
-        MachineId machineId = new MachineIdLinux();
-        ID = machineId.id();
-        // TODO ip 发生变化时会引发数据不一致
-        IPV4 = machineId.ipv4();
-    }
-
-    private final CPU cpu;
-    private final Memory memory;
-    private final Disk disk;
-    private final Network network;
-    private final OS os;
-
-    public Machine() {
-        ByteConverter byteConverter = new ByteConverter();
-        SystemInfo si = new SystemInfo();
-        HardwareAbstractionLayer hal = si.getHardware();
-        OperatingSystem operatingSystem = si.getOperatingSystem();
-
-        this.cpu = new CPU(si);
-        this.memory = new Memory(si, byteConverter);
-        this.disk = new Disk(si, byteConverter);
-        this.network = new Network(si, byteConverter);
-        this.os = new OS(si, byteConverter);
-    }
-
-    public EvtAgentStart detail() {
-        EvtAgentStart machineDetail = new EvtAgentStart();
-        machineDetail.setMachineId(Machine.ID);
-        machineDetail.setAppVersion(AppVersion.getVersion());
-
-        machineDetail.setOsDetail(os.detail());
-        List<NetworkDetail> list = network.detail().stream()
-                .peek(networkDetail -> {
-                    networkDetail.setPubicIpv4("0.0.0.0");
-                })
-                .collect(Collectors.toList());
-        machineDetail.setNetworkDetails(list);
-
-        machineDetail.setCpuDetail(cpu.detail());
-        machineDetail.setMemoryDetail(memory.detail());
-        machineDetail.setDiskDetails(disk.detail());
-        return machineDetail;
-    }
-
-    public EvtAgentHeartbeat stat() {
-        EvtAgentHeartbeat machineStat = new EvtAgentHeartbeat();
-        machineStat.setMachineId(Machine.ID);
-        machineStat.setMemoryDetail(memory.detail());
-        machineStat.setDiskDetails(disk.detail());
-        return machineStat;
-    }
-}

+ 0 - 76
common/src/main/java/cn/reghao/devops/common/machine/Memory.java

@@ -1,76 +0,0 @@
-package cn.reghao.devops.common.machine;
-
-import cn.reghao.devops.common.machine.model.MemoryInfo;
-import cn.reghao.devops.common.machine.model.MemoryStat;
-import cn.reghao.jutil.jdk.converter.ByteConverter;
-import cn.reghao.jutil.jdk.machine.data.MachineData;
-import cn.reghao.jutil.jdk.machine.data.detail.MemoryDetail;
-import cn.reghao.jutil.jdk.math.Calculator;
-import oshi.SystemInfo;
-import oshi.hardware.GlobalMemory;
-import oshi.hardware.VirtualMemory;
-
-/**
- * @author reghao
- * @date 2019-10-25 13:21:25
- */
-public class Memory implements MachineData<MemoryDetail, MemoryStat> {
-    private final GlobalMemory globalMemory;
-    private final VirtualMemory virtualMemory;
-    private MemoryDetail memoryDetail;
-    private ByteConverter byteConverter;
-
-    public Memory(SystemInfo si, ByteConverter byteConverter) {
-        GlobalMemory globalMemory = si.getHardware().getMemory();
-        this.globalMemory = globalMemory;
-        this.virtualMemory = globalMemory.getVirtualMemory();
-        this.byteConverter = byteConverter;
-    }
-
-    @Override
-    public MemoryDetail detail() {
-        long total = globalMemory.getTotal();
-        long avail = globalMemory.getAvailable();
-        long swapTotal = virtualMemory.getSwapTotal();
-        long swapUsed = virtualMemory.getSwapUsed();
-
-        if (memoryDetail == null) {
-            memoryDetail = new MemoryDetail(total, avail, total-avail, swapTotal, swapTotal-swapUsed);
-        }
-
-        return memoryDetail;
-    }
-
-    @Override
-    public MemoryStat stat() {
-        if (memoryDetail == null) {
-            memoryDetail = detail();
-        }
-
-        long total = globalMemory.getTotal();
-        long avail = globalMemory.getAvailable();
-        memoryDetail.setAvail(avail);
-        memoryDetail.setUsed(total-avail);
-
-        long swapTotal = virtualMemory.getSwapTotal();
-        long swapUsed = virtualMemory.getSwapUsed();
-        memoryDetail.setAvail(swapTotal-swapUsed);
-        return null;
-    }
-
-    public MemoryInfo getMemoryInfo() {
-        long total = globalMemory.getTotal();
-        String totalStr = byteConverter.convert(total);
-        long avail = globalMemory.getAvailable();
-        String availStr = byteConverter.convert(avail);
-        double availPercent = Calculator.divide(avail, total)*100;
-
-        long swapTotal = virtualMemory.getSwapTotal();
-        String swapTotalStr = byteConverter.convert(swapTotal);
-        long swapAvail = swapTotal - virtualMemory.getSwapUsed();
-        String swapAvailStr = byteConverter.convert(swapAvail);
-        double swapPercent = Calculator.divide(swapAvail, swapTotal)*100;
-        return new MemoryInfo(total, totalStr, avail, availStr, availPercent,
-                swapTotal, swapTotalStr, swapAvail, swapAvailStr, swapPercent);
-    }
-}

+ 0 - 107
common/src/main/java/cn/reghao/devops/common/machine/Network.java

@@ -1,107 +0,0 @@
-package cn.reghao.devops.common.machine;
-
-import cn.reghao.devops.common.machine.model.NetworkCard;
-import cn.reghao.jutil.jdk.converter.ByteConverter;
-import cn.reghao.jutil.jdk.machine.data.MachineData;
-import cn.reghao.jutil.jdk.machine.data.detail.NetworkDetail;
-import cn.reghao.jutil.jdk.machine.data.stat.NetworkStat;
-import oshi.SystemInfo;
-import oshi.hardware.HardwareAbstractionLayer;
-import oshi.software.os.OperatingSystem;
-
-import java.net.*;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Enumeration;
-import java.util.List;
-
-/**
- * @author reghao
- * @date 2021-10-16 18:45:39
- */
-public class Network implements MachineData<List<NetworkDetail>, NetworkStat> {
-    private final HardwareAbstractionLayer hal;
-    private OperatingSystem os;
-    private final ByteConverter byteConverter;
-
-    public Network(SystemInfo si, ByteConverter byteConverter) {
-        this.hal = si.getHardware();
-        this.os = si.getOperatingSystem();
-        this.byteConverter = byteConverter;
-    }
-
-    @Override
-    public List<NetworkDetail> detail() {
-        List<NetworkDetail> list = new ArrayList<>();
-        try {
-            Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
-            // 遍历主机的网络接口
-            while (interfaces.hasMoreElements()) {
-                NetworkInterface iface = interfaces.nextElement();
-                String ifaceName = iface.getName();
-                // 过滤掉 localhost 和虚拟网卡
-                if (ifaceName.startsWith("enp")
-                        || ifaceName.startsWith("wlp")
-                        || ifaceName.startsWith("eth")
-                        || ifaceName.startsWith("wlan")
-                        || ifaceName.startsWith("em")) {
-                    String ifaceMac = macAddr(iface.getHardwareAddress());
-                    NetworkDetail networkDetail = new NetworkDetail(ifaceName, ifaceMac);
-
-                    Enumeration<InetAddress> inetAddrs = iface.getInetAddresses();
-                    while (inetAddrs.hasMoreElements()) {
-                        InetAddress address = inetAddrs.nextElement();
-                        if (!address.isLoopbackAddress()) {
-                            if (address instanceof Inet4Address) {
-                                networkDetail.setIpv4(address.getHostAddress());
-                            } else if (address instanceof Inet6Address) {
-                                networkDetail.setIpv6(address.getHostAddress());
-                            }
-                        }
-                    }
-
-                    list.add(networkDetail);
-                }
-            }
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-        return list;
-    }
-
-    /**
-     * 返回 52-54-00-bf-ab-2d 格式的 MAC 地址
-     *
-     * @param
-     * @return
-     * @date 2020-10-14 下午1:53
-     */
-    private static String macAddr(byte[] addr) {
-        String[] hexadecimal = new String[addr.length];
-        for (int i = 0; i < addr.length; i++) {
-            hexadecimal[i] = String.format("%02x", addr[i]);
-        }
-        return String.join("-", hexadecimal);
-    }
-
-    @Override
-    public NetworkStat stat() {
-        return null;
-    }
-
-    public List<NetworkCard> getNetworkCards() {
-        List<NetworkCard> list = new ArrayList<>();
-        hal.getNetworkIFs().forEach(net -> {
-            String iface = net.getName();
-            String mac = net.getMacaddr();
-            String ipv4 = Arrays.toString(net.getIPv4addr());
-            String ipv6 = Arrays.toString(net.getIPv6addr());
-            long mtu = net.getMTU();
-            long speed = net.getSpeed();
-            String speedStr = byteConverter.convert(speed);
-            list.add(new NetworkCard(iface, mac, ipv4, ipv6, mtu, speed, speedStr));
-        });
-
-        return list;
-    }
-}

+ 0 - 40
common/src/main/java/cn/reghao/devops/common/machine/OS.java

@@ -1,40 +0,0 @@
-package cn.reghao.devops.common.machine;
-
-import cn.reghao.jutil.jdk.converter.ByteConverter;
-import cn.reghao.jutil.jdk.machine.data.MachineData;
-import cn.reghao.jutil.jdk.machine.data.detail.OsDetail;
-import cn.reghao.jutil.jdk.machine.data.stat.OsStat;
-import oshi.SystemInfo;
-import oshi.hardware.HardwareAbstractionLayer;
-import oshi.software.os.OperatingSystem;
-
-/**
- * @author reghao
- * @date 2020-10-20 23:17:30
- */
-public class OS implements MachineData<OsDetail, OsStat> {
-    private final HardwareAbstractionLayer hal;
-    private OperatingSystem os;
-    private final ByteConverter byteConverter;
-
-    public OS(SystemInfo si, ByteConverter byteConverter) {
-        this.hal = si.getHardware();
-        this.os = si.getOperatingSystem();
-        this.byteConverter = byteConverter;
-    }
-
-    @Override
-    public OsDetail detail() {
-        OsDetail osDetail = new OsDetail();
-        osDetail.setBootTime(os.getSystemBootTime());
-        return osDetail;
-    }
-
-    @Override
-    public OsStat stat() {
-        return null;
-    }
-
-    public void getOSInfo() {
-    }
-}

+ 0 - 8
common/src/main/java/cn/reghao/devops/common/machine/model/CPUInfo.java

@@ -1,8 +0,0 @@
-package cn.reghao.devops.common.machine.model;
-
-/**
- * @author reghao
- * @date 2025-09-26 17:29:00
- */
-public class CPUInfo {
-}

+ 0 - 31
common/src/main/java/cn/reghao/devops/common/machine/model/DiskInfo.java

@@ -1,31 +0,0 @@
-package cn.reghao.devops.common.machine.model;
-
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-import lombok.Setter;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * @author reghao
- * @date 2025-09-26 13:51:05
- */
-@AllArgsConstructor
-@Getter
-@Setter
-public class DiskInfo {
-    private String model;
-    private String name;
-    private long size;
-    private String sizeStr;
-    private List<DiskPartition> diskPartitionList;
-
-    public DiskInfo(String model, String name, long size, String sizeStr) {
-        this.model = model;
-        this.name = name;
-        this.size = size;
-        this.sizeStr = sizeStr;
-        this.diskPartitionList = new ArrayList<>();
-    }
-}

+ 0 - 40
common/src/main/java/cn/reghao/devops/common/machine/model/DiskPartition.java

@@ -1,40 +0,0 @@
-package cn.reghao.devops.common.machine.model;
-
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-import lombok.Setter;
-
-/**
- * @author reghao
- * @date 2025-09-26 13:51:15
- */
-@AllArgsConstructor
-@Getter
-@Setter
-public class DiskPartition {
-    private String diskName;
-    private long partitionSize;
-    private String uuid;
-    private String vol;
-    private String mountedOn;
-    private String fsType;
-    private long totalSpace;
-    private String totalSpaceStr;
-    private long freeSpace;
-    private String freeSpaceStr;
-    private double freeSpacePercent;
-    private long usableSpace;
-    private String usableSpaceStr;
-    private long totalInode;
-    private long freeInode;
-    private double freeInodePercent;
-
-    public DiskPartition(String diskName, long partitionSize, String uuid, String vol, String mountedOn, String fsType) {
-        this.diskName = diskName;
-        this.partitionSize = partitionSize;
-        this.uuid = uuid;
-        this.vol = vol;
-        this.mountedOn = mountedOn;
-        this.fsType = fsType;
-    }
-}

+ 0 - 28
common/src/main/java/cn/reghao/devops/common/machine/model/MemoryInfo.java

@@ -1,28 +0,0 @@
-package cn.reghao.devops.common.machine.model;
-
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-import lombok.NoArgsConstructor;
-import lombok.Setter;
-
-/**
- * @author reghao
- * @date 2025-09-26 17:29:17
- */
-@NoArgsConstructor
-@AllArgsConstructor
-@Getter
-@Setter
-public class MemoryInfo {
-    private long total;
-    private String totalStr;
-    private long avail;
-    private String availStr;
-    private double availPercent;
-
-    private long swapTotal;
-    private String swapTotalStr;
-    private long swapAvail;
-    private String swapAvailStr;
-    private double swapAvailPercent;
-}

+ 0 - 27
common/src/main/java/cn/reghao/devops/common/machine/model/MemoryStat.java

@@ -1,27 +0,0 @@
-package cn.reghao.devops.common.machine.model;
-
-import cn.reghao.jutil.jdk.converter.ByteConverter;
-import cn.reghao.jutil.jdk.converter.ByteType;
-import lombok.Data;
-
-import java.lang.management.MemoryUsage;
-
-/**
- * @author reghao
- * @date 2020-10-21 15:49:56
- */
-@Data
-public class MemoryStat {
-    private String init;
-    private String max;
-    private String used;
-    private String committed;
-
-    public MemoryStat(MemoryUsage memoryUsage) {
-        ByteConverter convert = new ByteConverter();
-        this.init = convert.convert(memoryUsage.getInit());
-        this.max = convert.convert(memoryUsage.getMax());
-        this.used = convert.convert(memoryUsage.getUsed());
-        this.committed = convert.convert(memoryUsage.getCommitted());
-    }
-}

+ 0 - 20
common/src/main/java/cn/reghao/devops/common/machine/model/NetworkCard.java

@@ -1,20 +0,0 @@
-package cn.reghao.devops.common.machine.model;
-
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-
-/**
- * @author reghao
- * @date 2025-09-26 16:59:22
- */
-@AllArgsConstructor
-@Getter
-public class NetworkCard {
-    private String iface;
-    private String mac;
-    private String ipv4;
-    private String ipv6;
-    private long mtu;
-    private long speed;
-    private String speedStr;
-}

+ 0 - 42
common/src/main/java/cn/reghao/devops/common/machine/model/SysProcess.java

@@ -1,42 +0,0 @@
-package cn.reghao.devops.common.machine.model;
-
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-import lombok.NoArgsConstructor;
-import lombok.Setter;
-
-import java.io.Serializable;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * @author reghao
- * @date 2025-12-17 14:34:09
- */
-@AllArgsConstructor
-@NoArgsConstructor
-@Setter
-@Getter
-public class SysProcess implements Serializable {
-    private static final long serialVersionUID = 1L;
-
-    private int pid;
-    private String name;
-    private int ppid;
-    private String cmdLine;
-    private Set<String> hostPorts;
-    private long startTime;
-    private String user;
-    private String containerId;
-    private String appId;
-
-    public SysProcess(int pid, String name, int ppid, String cmdLine, long startTime, String user) {
-        this.pid = pid;
-        this.name = name;
-        this.ppid = ppid;
-        this.cmdLine = cmdLine.length() > 255 ? cmdLine.substring(0, 255) : cmdLine;
-        this.hostPorts = new HashSet<>();
-        this.startTime = startTime;
-        this.user = user;
-    }
-}

+ 36 - 0
common/src/main/java/cn/reghao/devops/common/msg/AgentCommand.java

@@ -0,0 +1,36 @@
+package cn.reghao.devops.common.msg;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * @author reghao
+ * @date 2026-03-10 09:51:42
+ */
+public enum AgentCommand {
+    // 对 agent 注册的回应
+    @JsonProperty("REGISTER_ACK")
+    REGISTER_ACK,
+    // 对 agent 心跳的回应
+    @JsonProperty("HEARTBEAT_ACK")
+    HEARTBEAT_ACK,
+
+    @JsonProperty("DEPLOY_TASK")
+    DEPLOY_TASK,
+    @JsonProperty("APP_TASK")
+    APP_TASK,
+    @JsonProperty("UPGRADE_AGENT")
+    UPGRADE_AGENT,
+    @JsonProperty("CLEAN_IMAGE")
+    CLEAN_IMAGE,
+    @JsonProperty("UNKNOWN")
+    UNKNOWN;
+
+    // 静态工厂方法:安全解析,防止非法字符串导致异常
+    public static AgentCommand fromString(String value) {
+        try {
+            return AgentCommand.valueOf(value.toUpperCase());
+        } catch (Exception e) {
+            return UNKNOWN;
+        }
+    }
+}

+ 16 - 0
common/src/main/java/cn/reghao/devops/common/msg/AgentMessage.java

@@ -0,0 +1,16 @@
+package cn.reghao.devops.common.msg;
+
+import lombok.Data;
+
+/**
+ * mgr 发送给 agent 的消息格式
+ *
+ * @author reghao
+ * @date 2026-03-10 09:53:56
+ */
+@Data
+public class AgentMessage<T> {
+    private AgentCommand type;
+    private String taskId;
+    private T data;
+}

+ 25 - 0
common/src/main/java/cn/reghao/devops/common/msg/AgentResponse.java

@@ -0,0 +1,25 @@
+package cn.reghao.devops.common.msg;
+
+import lombok.*;
+
+/**
+ * agent 发送给 mgr 的消息格式
+ *
+ * @author reghao
+ * @date 2026-03-10 09:55:34
+ */
+@NoArgsConstructor
+@AllArgsConstructor
+@Setter
+@Getter
+public class AgentResponse<T> {
+    private AgentResponseType type;
+    private String agentId;
+    private String taskId; // 如果是任务相关的响应,必须带上任务ID
+    private T data;        // 具体的数据内容
+    private long timestamp;
+
+    public static <T> AgentResponse<T> ok(AgentResponseType type, String taskId, T data) {
+        return new AgentResponse<>(type, "agent-01", taskId, data, System.currentTimeMillis());
+    }
+}

+ 17 - 0
common/src/main/java/cn/reghao/devops/common/msg/AgentResponseType.java

@@ -0,0 +1,17 @@
+package cn.reghao.devops.common.msg;
+
+/**
+ * @author reghao
+ * @date 2026-03-10 09:55:27
+ */
+public enum AgentResponseType {
+    // 基础类
+    REGISTER,       // 启动时注册
+    HEARTBEAT,      // Agent 主动发起的心跳包
+    TASK_ERROR,     // 任务执行失败
+
+    // 任务反馈类
+    DEPLOY_TASK_ACK,       // 任务执行中(如:正在拉镜像、正在启动)
+    APP_TASK_ACK,         // 任务最终失败
+    CONTAINER_LOG,       // 容器日志流转发
+}

+ 17 - 0
common/src/main/java/cn/reghao/devops/common/msg/DeployStepName.java

@@ -0,0 +1,17 @@
+package cn.reghao.devops.common.msg;
+
+/**
+ * @author reghao
+ * @date 2026-03-10 09:39:30
+ */
+public enum DeployStepName {
+    PULL_IMAGE("拉取镜像"),
+    STOP_REMOVE_OLD("停止/删除旧容器"),
+    START_NEW("开启新容器"),
+    HEALTH_CHECK("健康检查"),
+    DEPLOY_DONE("部署完成");
+
+    private final String desc;
+    DeployStepName(String desc) { this.desc = desc; }
+    public String getDesc() { return desc; }
+}

+ 0 - 17
common/src/main/java/cn/reghao/devops/common/msg/MessageSender.java

@@ -1,17 +0,0 @@
-package cn.reghao.devops.common.msg;
-
-import java.io.IOException;
-
-/**
- * @author reghao
- * @date 2023-02-23 09:25:57
- */
-public interface MessageSender {
-    default void connect() {
-    }
-    void setConnected(boolean status);
-    boolean isConnected();
-    void send(String dest, Object message) throws IOException;
-    default void close() {
-    }
-}

+ 0 - 9
common/src/main/java/cn/reghao/devops/common/msg/constant/AppId.java

@@ -1,9 +0,0 @@
-package cn.reghao.devops.common.msg.constant;
-
-/**
- * @author reghao
- * @date 2021-08-27 17:32:08
- */
-public enum AppId {
-    mgr, build, agent
-}

+ 19 - 0
common/src/main/java/cn/reghao/devops/common/msg/constant/StepStatus.java

@@ -0,0 +1,19 @@
+package cn.reghao.devops.common.msg.constant;
+
+/**
+ * 阶段状态枚举
+ *
+ * @author reghao
+ * @date 2026-03-09 09:30:08
+ */
+public enum StepStatus {
+    PENDING("等待中"),
+    RUNNING("执行中"),
+    SUCCESS("成功"),
+    FAILURE("失败"),
+    SKIPPED("跳过");
+
+    private final String desc;
+    StepStatus(String desc) { this.desc = desc; }
+    public String getDesc() { return desc; }
+}

+ 30 - 0
common/src/main/java/cn/reghao/devops/common/msg/constant/WsClientType.java

@@ -0,0 +1,30 @@
+package cn.reghao.devops.common.msg.constant;
+/**     
+ * @author          reghao
+ * @date            2026-03-10 10:37:54
+ */
+public enum WsClientType {
+    /** 执行节点:来自各个服务器的 Agent */
+    AGENT("AGENT", 200_000L), // 200s 超时
+
+    /** 控制台:来自 Web 前端的浏览器连接 */
+    WEB("WEB", 120_000L);    // 120秒超时(前端允许更长的不活跃期)
+
+    private final String code;
+    private final long timeout; // 对应的心跳超时阈值
+
+    WsClientType(String code, long timeout) {
+        this.code = code;
+        this.timeout = timeout;
+    }
+
+    public String getCode() { return code; }
+    public long getTimeout() { return timeout; }
+
+    public static WsClientType fromCode(String code) {
+        for (WsClientType type : values()) {
+            if (type.code.equalsIgnoreCase(code)) return type;
+        }
+        return WEB; // 默认作为 WEB 端处理,或者抛出异常
+    }
+}

+ 0 - 26
common/src/main/java/cn/reghao/devops/common/msg/event/EvtAgentHeartbeat.java

@@ -1,26 +0,0 @@
-package cn.reghao.devops.common.msg.event;
-
-import cn.reghao.jutil.jdk.event.message.Event;
-import cn.reghao.jutil.jdk.machine.data.detail.DiskDetail;
-import cn.reghao.jutil.jdk.machine.data.detail.MemoryDetail;
-import lombok.Getter;
-import lombok.Setter;
-
-import java.util.List;
-
-/**
- * @author reghao
- * @date 2022-05-05 13:59:15
- */
-@Getter
-@Setter
-public class EvtAgentHeartbeat extends Event {
-    private String machineId;
-    private MemoryDetail memoryDetail;
-    private List<DiskDetail> diskDetails;
-    private Long timestamp;
-
-    public EvtAgentHeartbeat() {
-        this.timestamp = System.currentTimeMillis();
-    }
-}

+ 17 - 16
common/src/main/java/cn/reghao/devops/common/msg/event/EvtAgentStart.java

@@ -1,33 +1,34 @@
 package cn.reghao.devops.common.msg.event;
 
-import cn.reghao.jutil.jdk.web.result.AppVersion;
 import cn.reghao.jutil.jdk.event.message.Event;
-import cn.reghao.jutil.jdk.machine.data.detail.*;
 import lombok.Getter;
+import lombok.NoArgsConstructor;
 import lombok.Setter;
 
-import java.util.List;
-
 /**
  * @author reghao
  * @date 2022-05-05 13:59:15
  */
+@NoArgsConstructor
 @Getter
 @Setter
 public class EvtAgentStart extends Event {
-    @Deprecated
-    private String protocol;
     private String machineId;
-    private AppVersion appVersion;
-
-    private OsDetail osDetail;
-    private List<NetworkDetail> networkDetails;
-    private CpuDetail cpuDetail;
-    private MemoryDetail memoryDetail;
-    private List<DiskDetail> diskDetails;
-    private Long timestamp;
+    private String ipv4Address;
+    private String agentVersion;
+    private int cpuCore;
+    private long memTotal;
+    private String osArch;
+    private String osName;
+    private String osVersion;
+    private long bootTime;
 
-    public EvtAgentStart() {
-        this.timestamp = System.currentTimeMillis();
+    public EvtAgentStart(String machineId, String ipv4Address, String agentVersion) {
+        this.machineId = machineId;
+        this.ipv4Address = ipv4Address;
+        this.agentVersion = agentVersion;
+        this.osArch = System.getProperty("os.arch");
+        this.osName = System.getProperty("os.name");
+        this.osVersion = System.getProperty("os.version");
     }
 }

+ 1 - 0
common/src/main/java/cn/reghao/devops/common/msg/event/EvtAppDeploy.java

@@ -14,6 +14,7 @@ import lombok.Setter;
 public class EvtAppDeploy extends Event {
     private String packType;
     private String appId;
+    private int appPort;
     private String packagePath;
     private String startScript;
     private String startHome;

+ 9 - 3
common/src/main/java/cn/reghao/devops/common/msg/event/EvtAppStat.java

@@ -2,17 +2,23 @@ package cn.reghao.devops.common.msg.event;
 
 import cn.reghao.devops.common.msg.constant.AppStatOps;
 import cn.reghao.jutil.jdk.event.message.Event;
+import lombok.AllArgsConstructor;
 import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
 
 /**
  * @author reghao
  * @date 2023-03-06 16:04:48
  */
+@AllArgsConstructor
+@NoArgsConstructor
+@Setter
 @Getter
 public class EvtAppStat extends Event {
-    private final String packType;
-    private final String appId;
-    private final String ops;
+    private String packType;
+    private String appId;
+    private String ops;
 
     public EvtAppStat(String packType, String appId, AppStatOps appStatOps) {
         this.packType = packType;

+ 13 - 8
common/src/main/java/cn/reghao/devops/common/msg/event/EvtAppStatResult.java

@@ -1,32 +1,37 @@
 package cn.reghao.devops.common.msg.event;
 
+import cn.reghao.devops.common.msg.DeployStepName;
 import cn.reghao.jutil.jdk.event.message.Event;
-import cn.reghao.jutil.jdk.web.result.Result;
 import lombok.Getter;
+import lombok.NoArgsConstructor;
 import lombok.Setter;
 
-import java.time.LocalDateTime;
-
 /**
  * @author reghao
  * @date 2023-03-06 15:24:22
  */
+@NoArgsConstructor
 @Setter
 @Getter
 public class EvtAppStatResult extends Event {
     private String machineId;
     private String appId;
     private String commitId;
+    private long deployTime;
+    private String deployStepName;
+    private String deployStepStatus;
+    private String errorMsg;
     private Boolean running;
-    private LocalDateTime startTime;
-    private Integer pid;
-    private Boolean deploy;
-    private Result result;
+    private long startTime;
+    private int pid;
 
     public EvtAppStatResult(String appId, String machineId) {
         this.appId = appId;
         this.machineId = machineId;
+        this.deployTime = System.currentTimeMillis();
+        this.deployStepName = DeployStepName.DEPLOY_DONE.name();
         this.running = false;
-        this.deploy = false;
+        this.startTime = 0;
+        this.pid = 0;
     }
 }

+ 0 - 10
common/src/main/java/cn/reghao/devops/common/msg/event/EvtBuildApp.java

@@ -1,10 +0,0 @@
-package cn.reghao.devops.common.msg.event;
-
-import cn.reghao.jutil.jdk.event.message.Event;
-
-/**
- * @author reghao
- * @date 2023-03-16 16:21:35
- */
-public class EvtBuildApp extends Event {
-}

+ 0 - 10
common/src/main/java/cn/reghao/devops/common/msg/event/EvtBuildAppResult.java

@@ -1,10 +0,0 @@
-package cn.reghao.devops.common.msg.event;
-
-import cn.reghao.jutil.jdk.event.message.Event;
-
-/**
- * @author reghao
- * @date 2023-03-16 16:22:28
- */
-public class EvtBuildAppResult extends Event {
-}

+ 0 - 31
common/src/main/java/cn/reghao/devops/common/msg/event/EvtDockerOps.java

@@ -1,31 +0,0 @@
-package cn.reghao.devops.common.msg.event;
-
-import cn.reghao.devops.common.docker.model.DockerPayload;
-import cn.reghao.devops.common.docker.model.DockerQuery;
-import cn.reghao.jutil.jdk.event.message.Event;
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-import lombok.NoArgsConstructor;
-
-import java.util.List;
-
-/**
- * @author reghao
- * @date 2025-12-16 20:20:15
- */
-@NoArgsConstructor
-@AllArgsConstructor
-@Getter
-public class EvtDockerOps extends Event {
-    private String ops;
-    private String machineId;
-    private DockerQuery dockerQuery;
-    private List<String> payload;
-
-    public EvtDockerOps(DockerPayload dockerPayload) {
-        this.ops = dockerPayload.getOps();
-        this.machineId = dockerPayload.getMachineId();
-        this.dockerQuery = dockerPayload.getDockerQuery();
-        this.payload = dockerPayload.getPayload();
-    }
-}

+ 0 - 26
common/src/main/java/cn/reghao/devops/common/msg/event/EvtDockerOpsResult.java

@@ -1,26 +0,0 @@
-package cn.reghao.devops.common.msg.event;
-
-import cn.reghao.jutil.jdk.event.message.Event;
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-import lombok.Setter;
-
-import java.util.List;
-
-/**
- * @author reghao
- * @date 2025-12-16 20:20:29
- */
-@AllArgsConstructor
-@Setter
-@Getter
-public class EvtDockerOpsResult extends Event {
-    private String ops;
-    private String machineId;
-    private List<Object> resultList;
-
-    public EvtDockerOpsResult(String ops, String machineId) {
-        this.ops = ops;
-        this.machineId = machineId;
-    }
-}

+ 0 - 38
common/src/main/java/cn/reghao/devops/common/msg/event/EvtTaskResult.java

@@ -1,38 +0,0 @@
-package cn.reghao.devops.common.msg.event;
-
-import cn.reghao.devops.common.machine.model.SysProcess;
-import cn.reghao.jutil.jdk.event.message.Event;
-import lombok.Getter;
-import lombok.NoArgsConstructor;
-import lombok.Setter;
-
-import java.util.List;
-
-/**
- * @author reghao
- * @date 2025-12-16 10:45:44
- */
-@NoArgsConstructor
-@Setter
-@Getter
-public class EvtTaskResult extends Event {
-    private String machineId;
-    private String taskName;
-    private int resultType;
-    private String result;
-    private List<SysProcess> listenProcessList;
-
-    public EvtTaskResult(String machineId, String taskName, String result) {
-        this.machineId = machineId;
-        this.taskName = taskName;
-        this.resultType = 1;
-        this.result = result;
-    }
-
-    public EvtTaskResult(String machineId, String taskName, List<SysProcess> listenProcessList) {
-        this.machineId = machineId;
-        this.taskName = taskName;
-        this.resultType = 2;
-        this.listenProcessList = listenProcessList;
-    }
-}

+ 77 - 0
common/src/main/java/cn/reghao/devops/common/util/Disk.java

@@ -0,0 +1,77 @@
+package cn.reghao.devops.common.util;
+
+import oshi.SystemInfo;
+import oshi.software.os.OperatingSystem;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * @author reghao
+ * @date 2019-10-25 13:21:25
+ */
+public class Disk {
+    private final OperatingSystem os;
+
+    public Disk(SystemInfo si) {
+        this.os = si.getOperatingSystem();
+    }
+
+    public List<DiskDetail> detail() {
+        return os.getFileSystem().getFileStores().stream()
+                .filter(osFileStore -> osFileStore.getVolume().startsWith("/dev/"))
+                .map(osFileStore -> {
+                    String vol = osFileStore.getVolume();
+                    String mount = osFileStore.getMount();
+                    String fsType = osFileStore.getType();
+
+                    long total = osFileStore.getTotalSpace();
+                    // non-root 用户可用的磁盘空间
+                    long userAvail = osFileStore.getUsableSpace();
+                    // root 用户可用的磁盘空间
+                    long rootAvail = osFileStore.getFreeSpace();
+
+                    long inodeTotal = osFileStore.getTotalInodes();
+                    long inodeFree = osFileStore.getFreeInodes();
+
+                    return new DiskDetail(vol, mount, fsType, total, userAvail, total-userAvail, inodeTotal, inodeFree);
+                })
+                .collect(Collectors.toList());
+    }
+
+    /**
+     * 根据一个路径确定它所在的分区
+     *
+     * @param
+     * @return
+     * @date 2021-02-06 下午11:25
+     */
+    public DiskDetail diskDetail(String path) {
+        List<DiskDetail> diskInfos = detail();
+        // 根据挂载路径的长度降序
+        diskInfos.sort((o1, o2) -> o2.getMountedOn().length() - o1.getMountedOn().length());
+
+        List<DiskDetail> candidates = new ArrayList<>();
+        for (DiskDetail diskInfo : diskInfos) {
+            String mountedOn = diskInfo.getMountedOn();
+            String[] array = path.split(mountedOn);
+            if (array.length == 0) {
+                return diskInfo;
+            } else if (array.length == 2) {
+                if (array[1].startsWith("/") || "/".equals(mountedOn)) {
+                    candidates.add(diskInfo);
+                }
+            } else if (array.length > 2) {
+                candidates.add(diskInfo);
+            }
+        }
+
+        if (!candidates.isEmpty()) {
+            // TODO 如果 candidates 大于 0 则判断准确的那个
+            return candidates.get(0);
+        } else {
+            return null;
+        }
+    }
+}

+ 77 - 0
common/src/main/java/cn/reghao/devops/common/util/DiskDetail.java

@@ -0,0 +1,77 @@
+package cn.reghao.devops.common.util;
+
+import java.io.Serializable;
+
+/**
+ * @author reghao
+ * @date 2021-10-16 18:15:16
+ */
+public class DiskDetail implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    private String diskPath;
+    private String mountedOn;
+    private String fsType;
+    // bytes
+    private long total;
+    private long avail;
+    private long used;
+    private long inodeTotal;
+    private long inodeAvail;
+
+    public DiskDetail(String diskPath, String mountedOn, String fsType, long total, long avail, long used,
+                      long inodeTotal, long inodeAvail) {
+        this.diskPath = diskPath;
+        this.mountedOn = mountedOn;
+        this.fsType = fsType;
+        this.total = total;
+        this.avail = avail;
+        this.used = used;
+        this.inodeTotal = inodeTotal;
+        this.inodeAvail = inodeAvail;
+    }
+
+    public String getDiskPath() {
+        return diskPath;
+    }
+
+    public String getMountedOn() {
+        return mountedOn;
+    }
+
+    public String getFsType() {
+        return fsType;
+    }
+
+    public long getTotal() {
+        return total;
+    }
+
+    public void setAvail(long avail) {
+        this.avail = avail;
+    }
+
+    public long getAvail() {
+        return avail;
+    }
+
+    public void setUsed(long used) {
+        this.used = used;
+    }
+
+    public long getUsed() {
+        return used;
+    }
+
+    public long getInodeTotal() {
+        return inodeTotal;
+    }
+
+    public void setInodeAvail(long inodeAvail) {
+        this.inodeAvail = inodeAvail;
+    }
+
+    public long getInodeAvail() {
+        return inodeAvail;
+    }
+}

+ 56 - 0
common/src/main/java/cn/reghao/devops/common/util/JsonUtils.java

@@ -0,0 +1,56 @@
+package cn.reghao.devops.common.util;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import java.io.IOException;
+
+/**
+ * @author reghao
+ * @date 2026-03-10 09:50:05
+ */
+public class JsonUtils {
+    // 使用单例 ObjectMapper,保证性能和线程安全
+    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
+
+    static {
+        // 配置:忽略 JSON 中存在但 Java 对象中不存在的字段(防止报错)
+        OBJECT_MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+        // 配置:允许单引号、注释等非标准格式(可选,增加鲁棒性)
+        OBJECT_MAPPER.configure(com.fasterxml.jackson.core.JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
+    }
+
+    /**
+     * 将 JSON 字符串解析为 JsonNode 树结构
+     */
+    public static JsonNode readTree(String json) {
+        try {
+            return OBJECT_MAPPER.readTree(json);
+        } catch (IOException e) {
+            // 在 Agent 端,建议记录详细日志并抛出自定义运行时异常
+            throw new RuntimeException("JSON 解析失败: " + json, e);
+        }
+    }
+
+    /**
+     * 将 JsonNode 转换为具体的 Java 对象 (用于分发后的二次解析)
+     */
+    public static <T> T convert(JsonNode node, Class<T> clazz) {
+        try {
+            return OBJECT_MAPPER.treeToValue(node, clazz);
+        } catch (IOException e) {
+            throw new RuntimeException("JsonNode 转换对象失败", e);
+        }
+    }
+
+    public static String toJson(Object obj) {
+        if (obj == null) return null;
+        try {
+            return OBJECT_MAPPER.writeValueAsString(obj);
+        } catch (JsonProcessingException e) {
+            // 在 Agent 端通常抛出运行时异常,因为这属于编程错误
+            throw new RuntimeException("JSON 序列化失败: " + obj.getClass().getName(), e);
+        }
+    }
+}

+ 97 - 0
common/src/main/java/cn/reghao/devops/common/util/Machine.java

@@ -0,0 +1,97 @@
+package cn.reghao.devops.common.util;
+
+import cn.reghao.devops.common.msg.event.EvtAgentStart;
+import cn.reghao.jutil.jdk.web.result.AppVersion;
+import oshi.SystemInfo;
+import oshi.hardware.CentralProcessor;
+import oshi.hardware.GlobalMemory;
+import oshi.hardware.HardwareAbstractionLayer;
+import oshi.software.os.OperatingSystem;
+
+import java.io.IOException;
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+
+/**
+ * @author reghao
+ * @date 2020-10-22 15:47:58
+ */
+public class Machine {
+    public final static String ID;
+    public final static String IPV4;
+    public final static String AGENT_VERSION;
+    static {
+        ID = getMachineId();
+        IPV4 = getIpv4Address();
+        AGENT_VERSION = AppVersion.getVersion().getCommitId();
+    }
+    static SystemInfo si = new SystemInfo();
+    static HardwareAbstractionLayer hal = si.getHardware();
+    static OperatingSystem os = si.getOperatingSystem();
+
+    public static EvtAgentStart getEvtAgentStart() {
+        CentralProcessor centralProcessor = hal.getProcessor();
+        int cpuCore = centralProcessor.getLogicalProcessorCount();
+        GlobalMemory globalMemory = hal.getMemory();
+        long total = globalMemory.getTotal();
+        long bootTime = os.getSystemBootTime();
+
+        EvtAgentStart evtAgentStart = new EvtAgentStart(ID, IPV4, AGENT_VERSION);
+        evtAgentStart.setCpuCore(cpuCore);
+        evtAgentStart.setMemTotal(total);
+        evtAgentStart.setBootTime(bootTime);
+        return evtAgentStart;
+    }
+
+    public static String getIpv4Address() {
+        List<String> ipv4List = new ArrayList<>();
+        try {
+            Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
+            // 遍历主机的网络接口
+            while (interfaces.hasMoreElements()) {
+                NetworkInterface iface = interfaces.nextElement();
+                String ifaceName = iface.getName();
+                // 根据网络接口名前缀过滤掉 localhost 和虚拟网卡
+                if (ifaceName.startsWith("enp")
+                        || ifaceName.startsWith("wlp")
+                        || ifaceName.startsWith("eth")
+                        || ifaceName.startsWith("wlan")
+                        || ifaceName.startsWith("em")) {
+                    Enumeration<InetAddress> inetAddrs = iface.getInetAddresses();
+                    while (inetAddrs.hasMoreElements()) {
+                        InetAddress address = inetAddrs.nextElement();
+                        if (!address.isLoopbackAddress()) {
+                            if (address instanceof Inet4Address) {
+                                ipv4List.add(address.getHostAddress());
+                            }
+                        }
+                    }
+                }
+            }
+        } catch (Exception e) {
+            // ignore
+        }
+        return ipv4List.isEmpty() ? "127.0.0.1" : ipv4List.get(0);
+    }
+
+    public static String getMachineId() {
+        String path = "/etc/machine-id";
+        try {
+            // 读取所有行(machine-id 通常只有一行)
+            List<String> lines = Files.readAllLines(Paths.get(path));
+            if (!lines.isEmpty()) {
+                String machineId = lines.get(0).trim();
+                return machineId;
+            }
+        } catch (IOException e) {
+            // ignore
+        }
+        return "unknown";
+    }
+}

+ 0 - 90
common/src/main/java/cn/reghao/devops/common/ws/WebSocketListenerImpl.java

@@ -1,90 +0,0 @@
-package cn.reghao.devops.common.ws;
-
-import cn.reghao.jutil.jdk.serializer.JdkSerializer;
-import lombok.extern.slf4j.Slf4j;
-import okhttp3.Response;
-import okhttp3.WebSocket;
-import okhttp3.WebSocketListener;
-import okio.ByteString;
-
-import java.io.EOFException;
-import java.net.ConnectException;
-import java.net.ProtocolException;
-
-/**
- * @author reghao
- * @date 2023-02-23 09:26:50
- */
-@Slf4j
-public class WebSocketListenerImpl extends WebSocketListener {
-    private final WsClient wsClient;
-
-    public WebSocketListenerImpl(WsClient wsClient) {
-        this.wsClient = wsClient;
-    }
-
-    @Override
-    public void onOpen(WebSocket webSocket, Response response) {
-        log.info("WebSocket 连接成功");
-        wsClient.setConnected(true);
-        wsClient.resetRetryCount();
-    }
-
-    @Override
-    public void onClosing(WebSocket webSocket, int code, String reason) {
-        log.error("WebSocket 连接被动断开 -> {} - {}", code, reason);
-        wsClient.setConnected(false);
-
-        if (wsClient.isRetry()) {
-            reconnect();
-        }
-    }
-
-    @Override
-    public void onClosed(WebSocket webSocket, int code, String reason) {
-        log.error("WebSocket 连接主动断开 -> {} - {}", code, reason);
-        wsClient.setConnected(false);
-    }
-
-    @Override
-    public void onFailure(WebSocket webSocket, Throwable throwable, Response response) {
-        log.info("WebSocket 异常事件: {}", throwable.toString());
-        if (throwable instanceof ConnectException
-                || throwable instanceof EOFException
-                || throwable instanceof ProtocolException) {
-            wsClient.setConnected(false);
-            if (wsClient.isRetry()) {
-                reconnect();
-            }
-        } else {
-            throwable.printStackTrace();
-        }
-    }
-
-    private void reconnect() {
-        log.info("WebSocket 重连");
-        try {
-            if (wsClient.getRetryCount() > 10) {
-                log.info("WebSocket 重连超过 10 次, 休眠 1 分钟后再尝试");
-                Thread.sleep(60_000);
-                wsClient.resetRetryCount();
-            } else {
-                log.info("休眠 10s 后再尝试重连");
-                Thread.sleep(10_000);
-            }
-        } catch (InterruptedException e) {
-            e.printStackTrace();
-        }
-        wsClient.retryCountIncr();
-        wsClient.connect();
-    }
-
-    @Override
-    public void onMessage(WebSocket webSocket, String text) {
-    }
-
-    @Override
-    public void onMessage(WebSocket webSocket, ByteString bytes) {
-        Object object = JdkSerializer.deserialize(bytes.toByteArray());
-    }
-}

+ 0 - 106
common/src/main/java/cn/reghao/devops/common/ws/WsClient.java

@@ -1,106 +0,0 @@
-package cn.reghao.devops.common.ws;
-
-import cn.reghao.jutil.jdk.serializer.JdkSerializer;
-import cn.reghao.jutil.jdk.thread.ThreadPoolWrapper;
-import okhttp3.OkHttpClient;
-import okhttp3.Request;
-import okhttp3.WebSocket;
-import okhttp3.WebSocketListener;
-import okio.ByteString;
-
-import java.util.concurrent.Future;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-
-/**
- * @author reghao
- * @date 2023-02-23 09:26:50
- */
-public class WsClient {
-    private final ScheduledExecutorService scheduler = ThreadPoolWrapper.scheduledThreadPool("ws-heartbeat", 1);
-    private Future<?> heartbeatFuture;
-
-    private final String url;
-    private String token;
-    private WebSocket webSocket;
-    private boolean connected;
-    private final WebSocketListener webSocketListener;
-    private boolean retry;
-    private int retryCount;
-
-    public WsClient(String url) {
-        this.url = url;
-        //this.token = token;
-        this.webSocketListener = new WebSocketListenerImpl(this);
-        this.retry = true;
-        this.retryCount = 0;
-    }
-
-    public void setRetry(boolean retry) {
-        this.retry = retry;
-    }
-
-    public boolean isRetry() {
-        return retry;
-    }
-
-    public void retryCountIncr() {
-        this.retryCount += 1;
-    }
-
-    public void resetRetryCount() {
-        this.retryCount = 0;
-    }
-
-    public int getRetryCount() {
-        return retryCount;
-    }
-
-    public void connect() {
-        Request request = new Request.Builder()
-                .url(url)
-                //.header("Authorization", "Bearer " + token)
-                .build();
-
-        OkHttpClient okHttpClient = new OkHttpClient.Builder()
-                .connectTimeout(30, TimeUnit.SECONDS)
-                .readTimeout(30, TimeUnit.SECONDS)
-                .writeTimeout(30, TimeUnit.SECONDS)
-                .build();
-        this.webSocket = okHttpClient.newWebSocket(request, webSocketListener);
-    }
-
-    public void setConnected(boolean status) {
-        this.connected = status;
-        if (status) {
-            heartbeatFuture = scheduler.scheduleAtFixedRate(new HeartbeatTask(), 0, 10, TimeUnit.SECONDS);
-        } else {
-            if (heartbeatFuture != null && !heartbeatFuture.isCancelled()) {
-                heartbeatFuture.cancel(true);
-            }
-        }
-    }
-
-    public boolean isConnected() {
-        return connected;
-    }
-
-    public void send(Object message) {
-        if (isConnected()) {
-            byte[] bytes = JdkSerializer.serialize(message);
-            webSocket.send(ByteString.of(bytes));
-        }
-    }
-
-    public void close() {
-        setRetry(false);
-        webSocket.close(1000, "Client Close Connection");
-    }
-
-    class HeartbeatTask implements Runnable {
-        @Override
-        public void run() {
-            send("");
-        }
-    }
-}

+ 0 - 7
mgr/Dockerfile

@@ -1,7 +0,0 @@
-FROM adoptopenjdk/openjdk11:x86_64-debianslim-jdk-11.0.24_8-slim
-
-WORKDIR /app
-RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 'Asia/Shanghai' >/etc/timezone
-COPY bin/devopsmgr.jar /app/devopsmgr.jar
-
-ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-Djava.awt.headless=true","-jar","/app/devopsmgr.jar"]

+ 1 - 1
mgr/pom.xml

@@ -213,7 +213,7 @@
     </profiles>
 
     <build>
-        <finalName>devops${project.artifactId}</finalName>
+        <finalName>devops-${project.artifactId}</finalName>
         <resources>
             <resource>
                 <directory>src/main/resources</directory>

+ 4 - 7
mgr/src/main/java/cn/reghao/devops/mgr/admin/service/HomeViewService.java

@@ -1,5 +1,6 @@
 package cn.reghao.devops.mgr.admin.service;
 
+import cn.reghao.devops.common.util.Machine;
 import cn.reghao.devops.mgr.admin.model.vo.DashboardData;
 import cn.reghao.devops.mgr.admin.model.vo.SysInfo;
 import cn.reghao.devops.mgr.ops.machine.service.MachineQuery;
@@ -7,8 +8,6 @@ import cn.reghao.jutil.jdk.web.result.NotAvailable;
 import cn.reghao.devops.mgr.ops.machine.model.vo.MachineStat;
 import cn.reghao.jutil.jdk.jvm.JVM;
 import cn.reghao.jutil.jdk.jvm.model.JvmInfo;
-import cn.reghao.jutil.jdk.machine.id.MachineId;
-import cn.reghao.jutil.jdk.machine.id.MachineIdLinux;
 import org.springframework.stereotype.Service;
 import org.springframework.ui.Model;
 
@@ -21,14 +20,12 @@ import java.util.List;
 @Service
 public class HomeViewService {
     private final JVM jvm;
-    private final MachineId machineId;
     private final AppVersion appVersion;
     private final MachineQuery machineQuery;
     private final RoleService roleService;
 
     public HomeViewService(MachineQuery machineQuery, RoleService roleService) {
         this.jvm = new JVM();
-        this.machineId = new MachineIdLinux();
         this.appVersion = AppVersion.getVersion();
         this.machineQuery = machineQuery;
         this.roleService = roleService;
@@ -85,7 +82,7 @@ public class HomeViewService {
         String jvmInfo1 = String.format("%s %s", jvmInfo.getJvmName(), jvmInfo.getJvmVersion());
         int pid = jvmInfo.getJvmPid();
         String startAt = jvmInfo.getJvmStartTime();
-        String ipv4 = machineId.ipv4();
+        String ipv4 = Machine.IPV4;
 
         model.addAttribute("sysVersion", commitId);
         model.addAttribute("hostAddr", ipv4);
@@ -116,7 +113,7 @@ public class HomeViewService {
         String jvmInfo1 = String.format("%s %s", jvmInfo.getJvmName(), jvmInfo.getJvmVersion());
         int pid = jvmInfo.getJvmPid();
         String startAt = jvmInfo.getJvmStartTime();
-        String ipv4 = machineId.ipv4();
+        String ipv4 = Machine.IPV4;
         SysInfo sysInfo = new SysInfo(commitId, ipv4, osInfo, jvmInfo1, startAt, pid);
         return new DashboardData(machineStatList, sysInfo);
     }
@@ -136,7 +133,7 @@ public class HomeViewService {
         String jvmInfo1 = String.format("%s %s", jvmInfo.getJvmName(), jvmInfo.getJvmVersion());
         int pid = jvmInfo.getJvmPid();
         String startAt = jvmInfo.getJvmStartTime();
-        String ipv4 = machineId.ipv4();
+        String ipv4 = Machine.IPV4;
 
         model.addAttribute("sysVersion", commitId);
         model.addAttribute("hostAddr", ipv4);

+ 0 - 1
mgr/src/main/java/cn/reghao/devops/mgr/config/AccessInterceptor.java

@@ -1,6 +1,5 @@
 package cn.reghao.devops.mgr.config;
 
-import cn.reghao.devops.mgr.admin.service.*;
 import cn.reghao.devops.mgr.admin.service.LogService;
 import cn.reghao.jutil.jdk.http.HeaderNames;
 import lombok.extern.slf4j.Slf4j;

+ 3 - 28
mgr/src/main/java/cn/reghao/devops/mgr/config/spring/BeanConfig.java

@@ -1,6 +1,6 @@
 package cn.reghao.devops.mgr.config.spring;
 
-import cn.reghao.devops.common.machine.*;
+import cn.reghao.devops.common.util.Disk;
 import cn.reghao.jutil.jdk.converter.ByteConverter;
 import cn.reghao.jutil.jdk.http.WebClient;
 import cn.reghao.jutil.jdk.http.WebRequest;
@@ -9,8 +9,6 @@ import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.web.client.RestTemplate;
 import oshi.SystemInfo;
-import oshi.hardware.HardwareAbstractionLayer;
-import oshi.software.os.OperatingSystem;
 
 /**
  * @author reghao
@@ -41,34 +39,11 @@ public class BeanConfig {
 
     @Bean
     public SystemInfo systemInfo() {
-        SystemInfo si = new SystemInfo();
-        HardwareAbstractionLayer hal = si.getHardware();
-        OperatingSystem operatingSystem = si.getOperatingSystem();
-        return si;
-    }
-
-    @Bean
-    public CPU cpu() {
-        return new CPU(systemInfo());
-    }
-
-    @Bean
-    public Memory memory() {
-        return new Memory(systemInfo(), byteConverter());
+        return new SystemInfo();
     }
 
     @Bean
     public Disk disk() {
-        return new Disk(systemInfo(), byteConverter());
-    }
-
-    @Bean
-    public Network network() {
-        return new Network(systemInfo(), byteConverter());
-    }
-
-    @Bean
-    public OS os() {
-        return new OS(systemInfo(), byteConverter());
+        return new Disk(systemInfo());
     }
 }

+ 3 - 5
mgr/src/main/java/cn/reghao/devops/mgr/config/spring/SpringLifecycle.java

@@ -4,13 +4,12 @@ import ch.qos.logback.classic.Logger;
 import ch.qos.logback.classic.LoggerContext;
 import ch.qos.logback.classic.spi.ILoggingEvent;
 import ch.qos.logback.core.Appender;
+import cn.reghao.devops.common.util.Machine;
 import cn.reghao.devops.mgr.admin.service.LogService;
 import cn.reghao.devops.mgr.config.AppProperties;
 import cn.reghao.devops.mgr.config.log.MyLogbackAppender;
 import cn.reghao.devops.mgr.ops.machine.service.MachineService;
 import cn.reghao.devops.mgr.ops.util.BuilderInit;
-import cn.reghao.jutil.jdk.machine.id.MachineId;
-import cn.reghao.jutil.jdk.machine.id.MachineIdLinux;
 import lombok.extern.slf4j.Slf4j;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.DisposableBean;
@@ -53,7 +52,7 @@ public class SpringLifecycle implements ApplicationRunner, DisposableBean {
 
     @Override
     public void destroy() {
-        machineService.setMachineOffline();
+        //machineService.setMachineOffline();
         log.info("devopsmgr shutdown...");
     }
 
@@ -74,9 +73,8 @@ public class SpringLifecycle implements ApplicationRunner, DisposableBean {
         Logger rootLogger = loggerContext.getLogger("ROOT");
         Appender<ILoggingEvent> appender = rootLogger.getAppender("myLogbackAppender");
         if (appender instanceof MyLogbackAppender myLogbackAppender) {
-            MachineId machineId = new MachineIdLinux();
             String app = environment.getProperty("spring.application.name");
-            String host = machineId.ipv4();
+            String host = Machine.IPV4;
 
             myLogbackAppender.setApp(app);
             myLogbackAppender.setHost(host);

+ 23 - 14
mgr/src/main/java/cn/reghao/devops/mgr/ops/app/controller/BuildDeployController.java

@@ -1,10 +1,15 @@
 package cn.reghao.devops.mgr.ops.app.controller;
 
+import cn.reghao.devops.mgr.ops.app.model.po.PipelineStep;
 import cn.reghao.devops.mgr.ops.app.model.vo.AppBuildingVO;
 import cn.reghao.devops.mgr.ops.app.model.vo.AppDeployingNodeVO;
 import cn.reghao.devops.mgr.ops.app.model.vo.BuildLogVO;
 import cn.reghao.devops.mgr.ops.app.model.vo.BuildTask;
+import cn.reghao.devops.mgr.ops.app.service.BuildTimeMetrics;
+import cn.reghao.devops.mgr.ops.builder.BuildService;
+import cn.reghao.devops.mgr.util.JsonUtils;
 import cn.reghao.devops.mgr.util.StringUtil;
+import cn.reghao.jutil.jdk.serializer.JsonConverter;
 import cn.reghao.jutil.jdk.web.db.PageList;
 import cn.reghao.jutil.jdk.web.result.NotAvailable;
 import cn.reghao.devops.mgr.ops.app.service.AppBuildService;
@@ -14,10 +19,8 @@ import cn.reghao.devops.mgr.ops.app.service.webhook.WebhookService;
 import cn.reghao.devops.mgr.ops.app.db.query.AppBuildQuery;
 import cn.reghao.devops.mgr.ops.app.db.query.AppDeployQuery;
 import cn.reghao.devops.mgr.ops.app.model.po.log.BuildConfigSnapshot;
-import cn.reghao.devops.mgr.ops.app.model.po.log.BuildConsumed;
 import cn.reghao.devops.mgr.ops.app.model.po.log.BuildLog;
 import cn.reghao.devops.mgr.ops.app.service.bd.BuildApp;
-import cn.reghao.jutil.jdk.web.result.Result;
 import cn.reghao.jutil.web.WebResult;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import io.swagger.v3.oas.annotations.Operation;
@@ -28,9 +31,7 @@ import org.springframework.data.domain.Sort;
 import org.springframework.http.MediaType;
 import org.springframework.web.bind.annotation.*;
 
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 
 /**
  * @author reghao
@@ -48,11 +49,12 @@ public class BuildDeployController {
     private final GetApp getApp;
     private final AppBuildService appBuildService;
     private final WebhookService webhookService;
-    private int pageSize = 10;
+    private final int pageSize = 10;
+    private final BuildService buildService;
 
     public BuildDeployController(AppBuildQuery appBuildQuery, AppDeployQuery appDeployQuery, BuildApp buildApp,
                                  DeployApp deployApp, GetApp getApp, AppBuildService appBuildService,
-                                 WebhookService webhookService) {
+                                 WebhookService webhookService, BuildService buildService) {
         this.appBuildQuery = appBuildQuery;
         this.appDeployQuery = appDeployQuery;
         this.buildApp = buildApp;
@@ -60,6 +62,7 @@ public class BuildDeployController {
         this.getApp = getApp;
         this.appBuildService = appBuildService;
         this.webhookService = webhookService;
+        this.buildService = buildService;
     }
 
     @Operation(summary = "应用构建页面", description = "N")
@@ -159,7 +162,8 @@ public class BuildDeployController {
     @GetMapping("/config")
     public String buildConfigPage(@RequestParam("buildLogId") String buildLogId) {
         BuildLog buildLog = appBuildQuery.getBuildLog(buildLogId);
-        BuildConfigSnapshot buildConfigSnapshot = buildLog.getBuildConfigSnapshot();
+        String configSnapshot = buildLog.getConfigSnapshot();
+        BuildConfigSnapshot buildConfigSnapshot = JsonConverter.jsonToObject(configSnapshot, BuildConfigSnapshot.class);
         return WebResult.success(buildConfigSnapshot);
     }
 
@@ -167,22 +171,27 @@ public class BuildDeployController {
     @GetMapping("/consumed")
     public String buildTimePage(@RequestParam("buildLogId") String buildLogId) {
         BuildLog buildLog = appBuildQuery.getBuildLog(buildLogId);
-        BuildConsumed buildConsumed = buildLog.getBuildConsumed();
-        return WebResult.success(buildConsumed);
+        String stepJson = buildLog.getStepJson();
+        List<PipelineStep> list = JsonUtils.toList(stepJson, PipelineStep.class);
+        BuildTimeMetrics buildTimeMetrics = BuildTimeMetrics.calculate(list);
+        return WebResult.success(buildTimeMetrics);
     }
 
     @Operation(summary = "构建部署应用", description = "N")
     @PostMapping(value = "/update", produces = MediaType.APPLICATION_JSON_VALUE)
     public String buildAndDeploy(String appId) throws Exception {
-        Result result = buildApp.buildAndDeploy(appId, true);
-        return WebResult.result(result);
+        //Result result = buildApp.buildAndDeploy(appId, true);
+        buildService.build(appId, true);
+        return WebResult.success();
     }
 
     @Operation(summary = "构建应用", description = "N")
     @PostMapping(value = "/build", produces = MediaType.APPLICATION_JSON_VALUE)
     public String build(String appId) throws Exception {
-        Result result = buildApp.buildAndDeploy(appId, false);
-        return WebResult.result(result);
+        /*Result result = buildApp.buildAndDeploy(appId, false);
+        return WebBody.result(result);*/
+        buildService.build(appId, false);
+        return WebResult.success();
     }
 
     @Operation(summary = "部署应用", description = "N")

+ 1 - 2
mgr/src/main/java/cn/reghao/devops/mgr/ops/app/db/query/AppDeployQuery.java

@@ -1,6 +1,6 @@
 package cn.reghao.devops.mgr.ops.app.db.query;
 
-import cn.reghao.devops.common.agent.app.dto.AppInfo;
+import cn.reghao.devops.mgr.ops.app.model.vo.AppInfo;
 import cn.reghao.devops.mgr.ops.app.model.po.config.AppConfig;
 import cn.reghao.devops.mgr.ops.app.model.po.config.AppDeployConfig;
 import cn.reghao.devops.mgr.ops.app.model.vo.AppDeployingNodeVO;
@@ -18,7 +18,6 @@ import java.util.List;
 public interface AppDeployQuery {
     List<AppDeployConfig> getByAppId(String appId);
     List<AppDeployingNodeVO> get(String appId);
-    List<AppInfo> getAppsByMachineId(String machineId);
     AppDeployConfig getByAppIdAndMachineId(String appId, String machineId);
     boolean isDeploying(String appId, String machineId);
     int countByApp(AppConfig appConfig);

+ 18 - 11
mgr/src/main/java/cn/reghao/devops/mgr/ops/app/db/query/impl/AppBuildQueryImpl.java

@@ -1,11 +1,13 @@
 package cn.reghao.devops.mgr.ops.app.db.query.impl;
 
 import cn.reghao.devops.mgr.ops.app.db.query.AppBuildQuery;
-import cn.reghao.devops.mgr.ops.app.db.query.AppDeployQuery;
 import cn.reghao.devops.mgr.ops.app.db.repository.AppBuildingRepository;
+import cn.reghao.devops.mgr.ops.app.db.repository.AppDeployingRepository;
 import cn.reghao.devops.mgr.ops.app.db.repository.config.AppConfigRepository;
+import cn.reghao.devops.mgr.ops.app.db.repository.config.AppDeployConfigRepository;
 import cn.reghao.devops.mgr.ops.app.db.repository.log.BuildLogRepository;
 import cn.reghao.devops.mgr.ops.app.model.po.AppBuilding;
+import cn.reghao.devops.mgr.ops.app.model.po.AppDeploying;
 import cn.reghao.devops.mgr.ops.app.model.po.config.AppConfig;
 import cn.reghao.devops.mgr.ops.app.model.po.log.BuildLog;
 import cn.reghao.devops.mgr.ops.app.model.vo.AppBuildingVO;
@@ -35,14 +37,17 @@ public class AppBuildQueryImpl implements AppBuildQuery {
     private final AppConfigRepository appConfigRepository;
     private final AppBuildingRepository buildingRepository;
     private final BuildLogRepository buildLogRepository;
-    private final AppDeployQuery appDeployQuery;
+    private final AppDeployConfigRepository deployConfigRepository;
+    private final AppDeployingRepository deployingNodeRepository;
 
     public AppBuildQueryImpl(AppConfigRepository appConfigRepository, AppBuildingRepository buildingRepository,
-                             BuildLogRepository buildLogRepository, AppDeployQuery appDeployQuery) {
+                             BuildLogRepository buildLogRepository, AppDeployConfigRepository deployConfigRepository,
+                             AppDeployingRepository deployingNodeRepository) {
         this.appConfigRepository = appConfigRepository;
         this.buildingRepository = buildingRepository;
         this.buildLogRepository = buildLogRepository;
-        this.appDeployQuery = appDeployQuery;
+        this.deployConfigRepository = deployConfigRepository;
+        this.deployingNodeRepository = deployingNodeRepository;
     }
 
     @Override
@@ -87,7 +92,7 @@ public class AppBuildQueryImpl implements AppBuildQuery {
         Page<AppConfig> page = appConfigRepository.findByEnvAndAppType(env, type, pageable);
         List<AppConfigVO> list = page.stream()
                 .map(appConfig -> {
-                    int totalDeployNodes = appDeployQuery.countByApp(appConfig);
+                    int totalDeployNodes = deployConfigRepository.countByAppConfig(appConfig);
                     int totalDomains = 0;
                     String domains = appConfig.getDomains();
                     if (domains != null) {
@@ -121,14 +126,16 @@ public class AppBuildQueryImpl implements AppBuildQuery {
     }
 
     private AppBuildingVO getAppBuildingVO(AppBuilding appBuilding) {
-        int totalNode = appDeployQuery.countByApp(appBuilding.getAppConfig());
+        int totalNode = deployConfigRepository.countByAppConfig(appBuilding.getAppConfig());
         int totalRunning = 0;
         BuildLog buildLog = appBuilding.getBuildLog();
         if (buildLog != null) {
-            String buildLogId = buildLog.getBuildLogId();
-            totalRunning = appDeployQuery.getAppRunningCountByBuildLogId(buildLogId);
+            String appId = appBuilding.getAppConfig().getAppId();
+            long total = deployingNodeRepository.findByDeployConfig_AppConfig_AppId(appId).stream()
+                    .filter(AppDeploying::getRunning)
+                    .count();
+            totalRunning = (int) total;
         }
-
         return new AppBuildingVO(appBuilding, totalNode, totalRunning);
     }
 
@@ -160,7 +167,7 @@ public class AppBuildQueryImpl implements AppBuildQuery {
             return false;
         }
 
-        return appBuilding1.isOnBuilding();
+        return appBuilding1.getOnBuilding() != null && appBuilding1.getOnBuilding();
     }
 
     @Deprecated
@@ -183,7 +190,7 @@ public class AppBuildQueryImpl implements AppBuildQuery {
     public String getBuildResult(String buildLogId) {
         BuildLog buildLog = buildLogRepository.findByBuildLogId(buildLogId);
         if (buildLog != null) {
-            return buildLog.getMsg();
+            return buildLog.getLogSummary();
         }
         return "";
     }

Some files were not shown because too many files changed in this diff