From f416e63b661958ee8f89a0862834e5bccb1a6059 Mon Sep 17 00:00:00 2001 From: lijiahang Date: Tue, 7 May 2024 19:12:37 +0800 Subject: [PATCH] =?UTF-8?q?:sparkles:=20=E9=80=9A=E7=94=A8=E5=A4=A7?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E4=B8=8A=E4=BC=A0=E7=BB=84=E4=BB=B6.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../orion-ops-module-asset-service/pom.xml | 2 +- .../handler/ExecCommandAnsiHandler.java | 51 ++++--- .../model/TransferOperatorRequest.java | 4 +- .../model/TransferOperatorResponse.java | 4 +- .../ops/module/infra/api/FileUploadApi.java | 21 +++ .../orion-ops-module-infra-service/pom.xml | 6 + .../infra/api/impl/FileUploadApiImpl.java | 29 ++++ .../InfraWebSocketConfiguration.java | 39 ++++++ .../cache/FileUploadCacheKeyDefine.java | 27 ++++ .../infra/entity/dto/FileUploadTokenDTO.java | 33 +++++ .../upload/FileUploadMessageDispatcher.java | 82 +++++++++++ .../upload/enums/FileUploadOperatorType.java | 43 ++++++ .../upload/enums/FileUploadReceiverType.java | 48 +++++++ .../upload/handler/FileUploadHandler.java | 127 ++++++++++++++++++ .../upload/handler/IFileUploadHandler.java | 33 +++++ .../upload/model/FileUploadRequest.java | 29 ++++ .../upload/model/FileUploadResponse.java | 32 +++++ .../interceptor/FileUploadInterceptor.java | 51 +++++++ .../infra/service/FileUploadService.java | 31 +++++ .../service/impl/FileUploadServiceImpl.java | 47 +++++++ .../components/connect-session-table.vue | 4 +- 21 files changed, 711 insertions(+), 32 deletions(-) create mode 100644 orion-ops-module-infra/orion-ops-module-infra-provider/src/main/java/com/orion/ops/module/infra/api/FileUploadApi.java create mode 100644 orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/api/impl/FileUploadApiImpl.java create mode 100644 orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/configuration/InfraWebSocketConfiguration.java create mode 100644 orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/define/cache/FileUploadCacheKeyDefine.java create mode 100644 orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/entity/dto/FileUploadTokenDTO.java create mode 100644 orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/handler/upload/FileUploadMessageDispatcher.java create mode 100644 orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/handler/upload/enums/FileUploadOperatorType.java create mode 100644 orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/handler/upload/enums/FileUploadReceiverType.java create mode 100644 orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/handler/upload/handler/FileUploadHandler.java create mode 100644 orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/handler/upload/handler/IFileUploadHandler.java create mode 100644 orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/handler/upload/model/FileUploadRequest.java create mode 100644 orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/handler/upload/model/FileUploadResponse.java create mode 100644 orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/interceptor/FileUploadInterceptor.java create mode 100644 orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/service/FileUploadService.java create mode 100644 orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/service/impl/FileUploadServiceImpl.java diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/pom.xml b/orion-ops-module-asset/orion-ops-module-asset-service/pom.xml index 4c261f3b..b68fcf62 100644 --- a/orion-ops-module-asset/orion-ops-module-asset-service/pom.xml +++ b/orion-ops-module-asset/orion-ops-module-asset-service/pom.xml @@ -41,7 +41,7 @@ orion-ops-spring-boot-starter-web - + com.orion.ops orion-ops-spring-boot-starter-websocket diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/exec/command/handler/ExecCommandAnsiHandler.java b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/exec/command/handler/ExecCommandAnsiHandler.java index 42f1fd2e..744bd2e5 100644 --- a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/exec/command/handler/ExecCommandAnsiHandler.java +++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/exec/command/handler/ExecCommandAnsiHandler.java @@ -4,7 +4,6 @@ import com.orion.lang.support.timeout.TimeoutChecker; import com.orion.lang.support.timeout.TimeoutEndpoint; import com.orion.lang.utils.Booleans; import com.orion.lang.utils.ansi.AnsiAppender; -import com.orion.lang.utils.ansi.style.AnsiFont; import com.orion.lang.utils.ansi.style.color.AnsiForeground; import com.orion.lang.utils.time.Dates; import com.orion.net.host.ssh.ExitCode; @@ -31,16 +30,16 @@ public class ExecCommandAnsiHandler extends BaseExecCommandHandler { super.initLogOutputStream(); // 拼接启动日志 AnsiAppender appender = AnsiAppender.create() - .append(AnsiForeground.GREEN.and(AnsiFont.BOLD), "> 准备执行命令 ") + .append(AnsiForeground.BRIGHT_GREEN, "> 准备执行命令 ") .append(Dates.current()) .newLine() - .append(AnsiForeground.BLUE.and(AnsiFont.BOLD), "执行记录: ") + .append(AnsiForeground.BRIGHT_BLUE, "执行记录: ") .append(execCommand.getLogId()) .newLine() - .append(AnsiForeground.BLUE.and(AnsiFont.BOLD), "执行描述: ") + .append(AnsiForeground.BRIGHT_BLUE, "执行描述: ") .append(execCommand.getDescription()) .newLine() - .append(AnsiForeground.BLUE.and(AnsiFont.BOLD), "执行用户: ") + .append(AnsiForeground.BRIGHT_BLUE, "执行用户: ") .append(execCommand.getUsername()); // 非系统用户执行添加 userId if (Const.SYSTEM_USER_ID.equals(execCommand.getUserId())) { @@ -53,35 +52,35 @@ public class ExecCommandAnsiHandler extends BaseExecCommandHandler { } // 执行序列 if (execCommand.getExecSeq() != null) { - appender.append(AnsiForeground.BLUE.and(AnsiFont.BOLD), "执行序列: ") + appender.append(AnsiForeground.BRIGHT_BLUE, "执行序列: ") .append('#') .append(execCommand.getExecSeq()) .newLine(); } - appender.append(AnsiForeground.BLUE.and(AnsiFont.BOLD), "执行主机: ") + appender.append(AnsiForeground.BRIGHT_BLUE, "执行主机: ") .append(execHostCommand.getHostName()) .append(" (") .append(execHostCommand.getHostId()) .append(")") .newLine() - .append(AnsiForeground.BLUE.and(AnsiFont.BOLD), "主机地址: ") + .append(AnsiForeground.BRIGHT_BLUE, "主机地址: ") .append(execHostCommand.getHostAddress()) .newLine() - .append(AnsiForeground.BLUE.and(AnsiFont.BOLD), "超时时间: ") + .append(AnsiForeground.BRIGHT_BLUE, "超时时间: ") .append(execCommand.getTimeout()) .newLine() - .append(AnsiForeground.BLUE.and(AnsiFont.BOLD), "脚本执行: ") + .append(AnsiForeground.BRIGHT_BLUE, "脚本执行: ") .append(execCommand.getScriptExec()) .newLine() .newLine() - .append(AnsiForeground.GREEN.and(AnsiFont.BOLD), "> 执行命令 ") + .append(AnsiForeground.BRIGHT_GREEN, "> 执行命令 ") .newLine() .append(execHostCommand.getCommand()) .newLine() .newLine(); // 非脚本执行拼接开始执行日志 if (!Booleans.isTrue(execCommand.getScriptExec())) { - appender.append(AnsiForeground.GREEN.and(AnsiFont.BOLD), "> 开始执行命令 ") + appender.append(AnsiForeground.BRIGHT_GREEN, "> 开始执行命令 ") .append(Dates.current()) .newLine(); } @@ -94,10 +93,10 @@ public class ExecCommandAnsiHandler extends BaseExecCommandHandler { // 拼接上传日志 AnsiAppender startAppender = AnsiAppender.create() .newLine() - .append(AnsiForeground.GREEN.and(AnsiFont.BOLD), "> 准备上传脚本 ") + .append(AnsiForeground.BRIGHT_GREEN, "> 准备上传脚本 ") .append(Dates.current()) .newLine() - .append(AnsiForeground.BLUE.and(AnsiFont.BOLD), "文件路径: ") + .append(AnsiForeground.BRIGHT_BLUE, "文件路径: ") .append(execHostCommand.getScriptPath()) .newLine(); this.appendLog(startAppender); @@ -105,18 +104,18 @@ public class ExecCommandAnsiHandler extends BaseExecCommandHandler { super.uploadScriptFile(); // 拼接完成日志 AnsiAppender finishAppender = AnsiAppender.create() - .append(AnsiForeground.GREEN.and(AnsiFont.BOLD), "< 脚本上传成功 ") + .append(AnsiForeground.BRIGHT_GREEN, "< 脚本上传成功 ") .append(Dates.current()) .newLine() .newLine() - .append(AnsiForeground.GREEN.and(AnsiFont.BOLD), "> 开始执行脚本 ") + .append(AnsiForeground.BRIGHT_GREEN, "> 开始执行脚本 ") .append(Dates.current()) .newLine(); this.appendLog(finishAppender); } catch (Exception e) { // 拼接失败日志 AnsiAppender errorAppender = AnsiAppender.create() - .append(AnsiForeground.RED.and(AnsiFont.BOLD), "< 脚本上传失败 ") + .append(AnsiForeground.BRIGHT_RED, "< 脚本上传失败 ") .append(Dates.current()) .newLine(); this.appendLog(errorAppender); @@ -133,37 +132,37 @@ public class ExecCommandAnsiHandler extends BaseExecCommandHandler { .newLine(); if (this.status == ExecHostStatusEnum.INTERRUPTED) { // 中断执行 - appender.append(AnsiForeground.YELLOW.and(AnsiFont.BOLD), "< 命令执行中断 ") + appender.append(AnsiForeground.BRIGHT_YELLOW, "< 命令执行中断 ") .append(Dates.current()) .newLine(); } else if (this.status == ExecHostStatusEnum.FAILED) { // 执行失败 - appender.append(AnsiForeground.RED.and(AnsiFont.BOLD), "< 命令执行失败 ") + appender.append(AnsiForeground.BRIGHT_RED, "< 命令执行失败 ") .append(Dates.current()) .newLine() - .append(AnsiForeground.RED.and(AnsiFont.BOLD), "错误原因: ") + .append(AnsiForeground.BRIGHT_RED, "错误原因: ") .append(this.getErrorMessage(e)) .newLine(); } else if (this.status == ExecHostStatusEnum.TIMEOUT) { // 更新执行超时 - appender.append(AnsiForeground.YELLOW.and(AnsiFont.BOLD), "< 命令执行超时 ") + appender.append(AnsiForeground.BRIGHT_YELLOW, "< 命令执行超时 ") .append(Dates.current()) .newLine(); } else { long ms = this.updateRecord.getFinishTime().getTime() - this.updateRecord.getStartTime().getTime(); Integer exitStatus = this.updateRecord.getExitStatus(); // 执行完成 - appender.append(AnsiForeground.GREEN.and(AnsiFont.BOLD), "< 命令执行完成 ") + appender.append(AnsiForeground.BRIGHT_GREEN, "< 命令执行完成 ") .append(Dates.current()) .newLine() - .append(AnsiForeground.BLUE.and(AnsiFont.BOLD), "exit: "); + .append(AnsiForeground.BRIGHT_BLUE, "exit: "); if (ExitCode.isSuccess(exitStatus)) { - appender.append(AnsiForeground.GREEN.and(AnsiFont.BOLD), exitStatus); + appender.append(AnsiForeground.BRIGHT_GREEN, exitStatus); } else { - appender.append(AnsiForeground.RED.and(AnsiFont.BOLD), exitStatus); + appender.append(AnsiForeground.BRIGHT_RED, exitStatus); } appender.newLine() - .append(AnsiForeground.BLUE.and(AnsiFont.BOLD), "used: ") + .append(AnsiForeground.BRIGHT_BLUE, "used: ") .append(Dates.interval(ms, false, "d ", "h ", "m ", "s")) .append(" (") .append(ms) diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/transfer/model/TransferOperatorRequest.java b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/transfer/model/TransferOperatorRequest.java index c22e70e9..6312353f 100644 --- a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/transfer/model/TransferOperatorRequest.java +++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/transfer/model/TransferOperatorRequest.java @@ -2,9 +2,9 @@ package com.orion.ops.module.asset.handler.host.transfer.model; import io.swagger.v3.oas.annotations.media.Schema; import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; -import lombok.experimental.SuperBuilder; /** * 文件操作请求 实体对象 @@ -14,7 +14,7 @@ import lombok.experimental.SuperBuilder; * @since 2024/2/21 21:01 */ @Data -@SuperBuilder +@Builder @NoArgsConstructor @AllArgsConstructor @Schema(name = "FileOperatorRequest", description = "文件操作请求 实体对象") diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/transfer/model/TransferOperatorResponse.java b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/transfer/model/TransferOperatorResponse.java index b4463d0c..06bd3da4 100644 --- a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/transfer/model/TransferOperatorResponse.java +++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/transfer/model/TransferOperatorResponse.java @@ -2,9 +2,9 @@ package com.orion.ops.module.asset.handler.host.transfer.model; import io.swagger.v3.oas.annotations.media.Schema; import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; -import lombok.experimental.SuperBuilder; /** * 文件操作响应 实体对象 @@ -14,7 +14,7 @@ import lombok.experimental.SuperBuilder; * @since 2024/2/21 22:38 */ @Data -@SuperBuilder +@Builder @NoArgsConstructor @AllArgsConstructor @Schema(name = "FileOperatorResponse", description = "文件操作响应 实体对象") diff --git a/orion-ops-module-infra/orion-ops-module-infra-provider/src/main/java/com/orion/ops/module/infra/api/FileUploadApi.java b/orion-ops-module-infra/orion-ops-module-infra-provider/src/main/java/com/orion/ops/module/infra/api/FileUploadApi.java new file mode 100644 index 00000000..e94f5838 --- /dev/null +++ b/orion-ops-module-infra/orion-ops-module-infra-provider/src/main/java/com/orion/ops/module/infra/api/FileUploadApi.java @@ -0,0 +1,21 @@ +package com.orion.ops.module.infra.api; + +/** + * 文件上传 对外服务类 + * + * @author Jiahang Li + * @version 1.0.0 + * @since 2024/5/7 15:58 + */ +public interface FileUploadApi { + + /** + * 生成上传 uploadToken + * + * @param userId userId + * @param endpoint endpoint + * @return token + */ + String createUploadToken(Long userId, String endpoint); + +} diff --git a/orion-ops-module-infra/orion-ops-module-infra-service/pom.xml b/orion-ops-module-infra/orion-ops-module-infra-service/pom.xml index aacc5b2c..910fab80 100644 --- a/orion-ops-module-infra/orion-ops-module-infra-service/pom.xml +++ b/orion-ops-module-infra/orion-ops-module-infra-service/pom.xml @@ -34,6 +34,12 @@ orion-ops-spring-boot-starter-web + + + com.orion.ops + orion-ops-spring-boot-starter-websocket + + com.orion.ops diff --git a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/api/impl/FileUploadApiImpl.java b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/api/impl/FileUploadApiImpl.java new file mode 100644 index 00000000..79a5178f --- /dev/null +++ b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/api/impl/FileUploadApiImpl.java @@ -0,0 +1,29 @@ +package com.orion.ops.module.infra.api.impl; + +import com.orion.ops.module.infra.api.FileUploadApi; +import com.orion.ops.module.infra.service.FileUploadService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; + +/** + * 文件上传 对外服务实现类 + * + * @author Jiahang Li + * @version 1.0.0 + * @since 2024/5/7 19:04 + */ +@Slf4j +@Service +public class FileUploadApiImpl implements FileUploadApi { + + @Resource + private FileUploadService fileUploadService; + + @Override + public String createUploadToken(Long userId, String endpoint) { + return fileUploadService.createUploadToken(userId, endpoint); + } + +} diff --git a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/configuration/InfraWebSocketConfiguration.java b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/configuration/InfraWebSocketConfiguration.java new file mode 100644 index 00000000..90d31998 --- /dev/null +++ b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/configuration/InfraWebSocketConfiguration.java @@ -0,0 +1,39 @@ +package com.orion.ops.module.infra.configuration; + +import com.orion.ops.module.infra.handler.upload.FileUploadMessageDispatcher; +import com.orion.ops.module.infra.interceptor.FileUploadInterceptor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.socket.config.annotation.WebSocketConfigurer; +import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; + +import javax.annotation.Resource; + +/** + * 基建模块 websocket 配置 + * + * @author Jiahang Li + * @version 1.0.0 + * @since 2023/12/28 11:39 + */ +@Configuration +public class InfraWebSocketConfiguration implements WebSocketConfigurer { + + @Value("${orion.websocket.prefix}") + private String prefix; + + @Resource + private FileUploadInterceptor fileUploadInterceptor; + + @Resource + private FileUploadMessageDispatcher fileUploadMessageDispatcher; + + @Override + public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { + // 文件上传 + registry.addHandler(fileUploadMessageDispatcher, prefix + "/file/upload/{uploadToken}") + .addInterceptors(fileUploadInterceptor) + .setAllowedOrigins("*"); + } + +} diff --git a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/define/cache/FileUploadCacheKeyDefine.java b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/define/cache/FileUploadCacheKeyDefine.java new file mode 100644 index 00000000..5e64fba5 --- /dev/null +++ b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/define/cache/FileUploadCacheKeyDefine.java @@ -0,0 +1,27 @@ +package com.orion.ops.module.infra.define.cache; + +import com.orion.lang.define.cache.key.CacheKeyBuilder; +import com.orion.lang.define.cache.key.CacheKeyDefine; +import com.orion.lang.define.cache.key.struct.RedisCacheStruct; +import com.orion.ops.module.infra.entity.dto.FileUploadTokenDTO; + +import java.util.concurrent.TimeUnit; + +/** + * 文明上传缓存 key + * + * @author Jiahang Li + * @version 1.0.0 + * @since 2023/8/31 11:48 + */ +public interface FileUploadCacheKeyDefine { + + CacheKeyDefine FILE_UPLOAD = new CacheKeyBuilder() + .key("file:upload:{}") + .desc("文件上传信息 ${token}") + .type(FileUploadTokenDTO.class) + .struct(RedisCacheStruct.STRING) + .timeout(3, TimeUnit.MINUTES) + .build(); + +} diff --git a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/entity/dto/FileUploadTokenDTO.java b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/entity/dto/FileUploadTokenDTO.java new file mode 100644 index 00000000..b81833d4 --- /dev/null +++ b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/entity/dto/FileUploadTokenDTO.java @@ -0,0 +1,33 @@ +package com.orion.ops.module.infra.entity.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 文件上传 token 缓存对象 + * + * @author Jiahang Li + * @version 1.0.0 + * @since 2023-7-13 18:42 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Schema(name = "FileUploadTokenDTO", description = "文件上传 token 缓存对象") +public class FileUploadTokenDTO implements Serializable { + + private static final long serialVersionUID = 1L; + + @Schema(description = "userId") + private Long userId; + + @Schema(description = "上传父目录") + private String endpoint; + +} diff --git a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/handler/upload/FileUploadMessageDispatcher.java b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/handler/upload/FileUploadMessageDispatcher.java new file mode 100644 index 00000000..26077f7a --- /dev/null +++ b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/handler/upload/FileUploadMessageDispatcher.java @@ -0,0 +1,82 @@ +package com.orion.ops.module.infra.handler.upload; + +import com.alibaba.fastjson.JSON; +import com.orion.lang.utils.io.Streams; +import com.orion.ops.framework.common.constant.ExtraFieldConst; +import com.orion.ops.framework.common.file.FileClient; +import com.orion.ops.framework.websocket.core.utils.WebSockets; +import com.orion.ops.module.infra.entity.dto.FileUploadTokenDTO; +import com.orion.ops.module.infra.handler.upload.enums.FileUploadOperatorType; +import com.orion.ops.module.infra.handler.upload.handler.FileUploadHandler; +import com.orion.ops.module.infra.handler.upload.handler.IFileUploadHandler; +import com.orion.ops.module.infra.handler.upload.model.FileUploadRequest; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.web.socket.BinaryMessage; +import org.springframework.web.socket.CloseStatus; +import org.springframework.web.socket.TextMessage; +import org.springframework.web.socket.WebSocketSession; +import org.springframework.web.socket.handler.AbstractWebSocketHandler; + +import javax.annotation.Resource; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 上传消息处理器 + * + * @author Jiahang Li + * @version 1.0.0 + * @since 2024/2/21 18:22 + */ +@Slf4j +@Component +public class FileUploadMessageDispatcher extends AbstractWebSocketHandler { + + private final ConcurrentHashMap handlers = new ConcurrentHashMap<>(); + + @Resource + private FileClient localFileClient; + + @Override + protected void handleTextMessage(WebSocketSession session, TextMessage message) { + // 获取处理器 + IFileUploadHandler handler = handlers.computeIfAbsent(session.getId(), s -> { + FileUploadTokenDTO info = WebSockets.getAttr(session, ExtraFieldConst.INFO); + return new FileUploadHandler(session, localFileClient, info.getEndpoint()); + }); + // 处理消息 + FileUploadRequest request = JSON.parseObject(message.getPayload(), FileUploadRequest.class); + FileUploadOperatorType type = FileUploadOperatorType.of(request.getType()); + if (FileUploadOperatorType.START.equals(type)) { + // 开始上传 + handler.start(request.getFileId()); + } else if (FileUploadOperatorType.FINISH.equals(type)) { + // 上传完成 + handler.finish(); + } + } + + @Override + protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) { + handlers.get(session.getId()).write(message.getPayload().array()); + } + + @Override + public void afterConnectionEstablished(WebSocketSession session) { + log.info("FileUploadMessageDispatcher-afterConnectionEstablished id: {}", session.getId()); + } + + @Override + public void handleTransportError(WebSocketSession session, Throwable exception) { + log.error("FileUploadMessageDispatcher-handleTransportError id: {}", session.getId(), exception); + } + + @Override + public void afterConnectionClosed(WebSocketSession session, CloseStatus status) { + String id = session.getId(); + log.info("FileUploadMessageDispatcher-afterConnectionClosed id: {}, code: {}, reason: {}", id, status.getCode(), status.getReason()); + // 关闭会话 + Streams.close(handlers.remove(id)); + } + +} diff --git a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/handler/upload/enums/FileUploadOperatorType.java b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/handler/upload/enums/FileUploadOperatorType.java new file mode 100644 index 00000000..f1545861 --- /dev/null +++ b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/handler/upload/enums/FileUploadOperatorType.java @@ -0,0 +1,43 @@ +package com.orion.ops.module.infra.handler.upload.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 文件上传操作类型 + * + * @author Jiahang Li + * @version 1.0.0 + * @since 2024/5/7 18:01 + */ +@Getter +@AllArgsConstructor +public enum FileUploadOperatorType { + + /** + * 开始上传 + */ + START("start"), + + /** + * 上传完成 + */ + FINISH("finish"), + + ; + + private final String type; + + public static FileUploadOperatorType of(String type) { + if (type == null) { + return null; + } + for (FileUploadOperatorType value : values()) { + if (value.type.equals(type)) { + return value; + } + } + return null; + } + +} diff --git a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/handler/upload/enums/FileUploadReceiverType.java b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/handler/upload/enums/FileUploadReceiverType.java new file mode 100644 index 00000000..2fd70202 --- /dev/null +++ b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/handler/upload/enums/FileUploadReceiverType.java @@ -0,0 +1,48 @@ +package com.orion.ops.module.infra.handler.upload.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 文件上传响应类型 + * + * @author Jiahang Li + * @version 1.0.0 + * @since 2024/5/7 18:01 + */ +@Getter +@AllArgsConstructor +public enum FileUploadReceiverType { + + /** + * 请求下一块数据 + */ + NEXT("next"), + + /** + * 上传完成 + */ + FINISH("finish"), + + /** + * 上传失败 + */ + ERROR("error"), + + ; + + private final String type; + + public static FileUploadReceiverType of(String type) { + if (type == null) { + return null; + } + for (FileUploadReceiverType value : values()) { + if (value.type.equals(type)) { + return value; + } + } + return null; + } + +} diff --git a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/handler/upload/handler/FileUploadHandler.java b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/handler/upload/handler/FileUploadHandler.java new file mode 100644 index 00000000..6d76a0e8 --- /dev/null +++ b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/handler/upload/handler/FileUploadHandler.java @@ -0,0 +1,127 @@ +package com.orion.ops.module.infra.handler.upload.handler; + +import com.alibaba.fastjson.JSON; +import com.orion.lang.utils.io.Streams; +import com.orion.ops.framework.common.constant.Const; +import com.orion.ops.framework.common.file.FileClient; +import com.orion.ops.framework.websocket.core.utils.WebSockets; +import com.orion.ops.module.infra.handler.upload.enums.FileUploadReceiverType; +import com.orion.ops.module.infra.handler.upload.model.FileUploadResponse; +import org.springframework.web.socket.WebSocketSession; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * 文件上传处理器实现 + * + * @author Jiahang Li + * @version 1.0.0 + * @since 2024/5/7 15:49 + */ +public class FileUploadHandler implements IFileUploadHandler { + + private final WebSocketSession channel; + + private final FileClient fileClient; + + private final String endpoint; + + private String fileId; + + private String filePath; + + private OutputStream outputStream; + + private boolean closed; + + public FileUploadHandler(WebSocketSession channel, FileClient fileClient, String endpoint) { + this.channel = channel; + this.fileClient = fileClient; + this.endpoint = endpoint; + } + + @Override + public void start(String fileId) { + // 释放资源 + this.close(); + // 获取返回路径 + this.fileId = fileId; + this.filePath = fileClient.getReturnPath(endpoint + Const.SLASH + fileId); + try { + // 打开文件流 + this.outputStream = fileClient.getContentOutputStream(filePath); + this.closed = false; + // 请求下一块数据 + FileUploadResponse resp = FileUploadResponse.builder() + .type(FileUploadReceiverType.NEXT.getType()) + .fileId(this.fileId) + .build(); + this.send(resp); + } catch (Exception e) { + // 释放资源 + this.close(); + // 返回错误 + FileUploadResponse resp = FileUploadResponse.builder() + .type(FileUploadReceiverType.ERROR.getType()) + .fileId(this.fileId) + .build(); + this.send(resp); + } + } + + @Override + public void write(byte[] content) { + try { + // 写入内容 + this.outputStream.write(content); + // 请求下一块数据 + FileUploadResponse resp = FileUploadResponse.builder() + .type(FileUploadReceiverType.NEXT.getType()) + .fileId(this.fileId) + .build(); + this.send(resp); + } catch (IOException e) { + // 释放资源 + this.close(); + // 返回错误 + FileUploadResponse resp = FileUploadResponse.builder() + .type(FileUploadReceiverType.ERROR.getType()) + .fileId(this.fileId) + .build(); + this.send(resp); + } + } + + @Override + public void finish() { + // 释放资源 + this.close(); + // 返回上传路径 + FileUploadResponse resp = FileUploadResponse.builder() + .type(FileUploadReceiverType.FINISH.getType()) + .fileId(this.fileId) + .path(this.filePath) + .build(); + this.send(resp); + } + + @Override + public void close() { + if (closed) { + return; + } + this.closed = true; + Streams.close(outputStream); + } + + /** + * 发送消息 + * + * @param resp resp + */ + private void send(FileUploadResponse resp) { + WebSockets.sendText(channel, JSON.toJSONString(resp)); + } + +} diff --git a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/handler/upload/handler/IFileUploadHandler.java b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/handler/upload/handler/IFileUploadHandler.java new file mode 100644 index 00000000..3afe4aa6 --- /dev/null +++ b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/handler/upload/handler/IFileUploadHandler.java @@ -0,0 +1,33 @@ +package com.orion.ops.module.infra.handler.upload.handler; + +import com.orion.lang.able.SafeCloseable; + +/** + * 文件上传处理器 + * + * @author Jiahang Li + * @version 1.0.0 + * @since 2024/5/7 15:49 + */ +public interface IFileUploadHandler extends SafeCloseable { + + /** + * 开始上传 + * + * @param fileId fileId + */ + void start(String fileId); + + /** + * 写入内容 + * + * @param content content + */ + void write(byte[] content); + + /** + * 上传结束 + */ + void finish(); + +} diff --git a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/handler/upload/model/FileUploadRequest.java b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/handler/upload/model/FileUploadRequest.java new file mode 100644 index 00000000..6b90f4be --- /dev/null +++ b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/handler/upload/model/FileUploadRequest.java @@ -0,0 +1,29 @@ +package com.orion.ops.module.infra.handler.upload.model; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 文件上传请求 实体对象 + * + * @author Jiahang Li + * @version 1.0.0 + * @since 2024/5/7 18:12 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Schema(name = "FileUploadRequest", description = "文件上传请求 实体对象") +public class FileUploadRequest { + + @Schema(description = "type") + private String type; + + @Schema(description = "fileId") + private String fileId; + +} diff --git a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/handler/upload/model/FileUploadResponse.java b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/handler/upload/model/FileUploadResponse.java new file mode 100644 index 00000000..3bf61a7b --- /dev/null +++ b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/handler/upload/model/FileUploadResponse.java @@ -0,0 +1,32 @@ +package com.orion.ops.module.infra.handler.upload.model; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 文件上传响应 实体对象 + * + * @author Jiahang Li + * @version 1.0.0 + * @since 2024/5/7 18:12 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Schema(name = "FileUploadResponse", description = "文件上传响应 实体对象") +public class FileUploadResponse { + + @Schema(description = "type") + private String type; + + @Schema(description = "fileId") + private String fileId; + + @Schema(description = "路径") + private String path; + +} diff --git a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/interceptor/FileUploadInterceptor.java b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/interceptor/FileUploadInterceptor.java new file mode 100644 index 00000000..aa663e82 --- /dev/null +++ b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/interceptor/FileUploadInterceptor.java @@ -0,0 +1,51 @@ +package com.orion.ops.module.infra.interceptor; + +import com.orion.lang.utils.Urls; +import com.orion.ops.framework.common.constant.ExtraFieldConst; +import com.orion.ops.module.infra.entity.dto.FileUploadTokenDTO; +import com.orion.ops.module.infra.service.FileUploadService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.server.ServerHttpRequest; +import org.springframework.http.server.ServerHttpResponse; +import org.springframework.stereotype.Component; +import org.springframework.web.socket.WebSocketHandler; +import org.springframework.web.socket.server.HandshakeInterceptor; + +import javax.annotation.Resource; +import java.util.Map; + +/** + * 文件上传拦截器 + * + * @author Jiahang Li + * @version 1.0.0 + * @since 2023/12/27 23:53 + */ +@Slf4j +@Component +public class FileUploadInterceptor implements HandshakeInterceptor { + + @Resource + private FileUploadService fileUploadService; + + @Override + public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map attributes) throws Exception { + // 获取 uploadToken + String uploadToken = Urls.getUrlSource(request.getURI().getPath()); + log.info("FileUploadInterceptor-beforeHandshake start uploadToken: {}", uploadToken); + // 检查 uploadToken + FileUploadTokenDTO info = fileUploadService.checkUploadToken(uploadToken); + if (info == null) { + log.error("FileUploadInterceptor-beforeHandshake absent uploadToken: {}", uploadToken); + return false; + } + // 设置参数 + attributes.put(ExtraFieldConst.INFO, info); + return true; + } + + @Override + public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) { + } + +} diff --git a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/service/FileUploadService.java b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/service/FileUploadService.java new file mode 100644 index 00000000..1d8aa1cf --- /dev/null +++ b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/service/FileUploadService.java @@ -0,0 +1,31 @@ +package com.orion.ops.module.infra.service; + +import com.orion.ops.module.infra.entity.dto.FileUploadTokenDTO; + +/** + * 文件上传服务 + * + * @author Jiahang Li + * @version 1.0.0 + * @since 2024/5/7 15:58 + */ +public interface FileUploadService { + + /** + * 生成上传 uploadToken + * + * @param userId userId + * @param endpoint endpoint + * @return token + */ + String createUploadToken(Long userId, String endpoint); + + /** + * 检查 uploadToken + * + * @param token token + * @return info + */ + FileUploadTokenDTO checkUploadToken(String token); + +} diff --git a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/service/impl/FileUploadServiceImpl.java b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/service/impl/FileUploadServiceImpl.java new file mode 100644 index 00000000..0b98b909 --- /dev/null +++ b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/service/impl/FileUploadServiceImpl.java @@ -0,0 +1,47 @@ +package com.orion.ops.module.infra.service.impl; + +import com.orion.lang.id.UUIds; +import com.orion.ops.framework.redis.core.utils.RedisStrings; +import com.orion.ops.module.infra.define.cache.FileUploadCacheKeyDefine; +import com.orion.ops.module.infra.entity.dto.FileUploadTokenDTO; +import com.orion.ops.module.infra.service.FileUploadService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +/** + * 文件上传服务 + * + * @author Jiahang Li + * @version 1.0.0 + * @since 2024/5/7 16:43 + */ +@Slf4j +@Service +public class FileUploadServiceImpl implements FileUploadService { + + @Override + public String createUploadToken(Long userId, String endpoint) { + String token = UUIds.random32(); + String key = FileUploadCacheKeyDefine.FILE_UPLOAD.format(token); + // 设置缓存 + FileUploadTokenDTO info = FileUploadTokenDTO.builder() + .userId(userId) + .endpoint(endpoint) + .build(); + RedisStrings.setJson(key, FileUploadCacheKeyDefine.FILE_UPLOAD, info); + return token; + } + + @Override + public FileUploadTokenDTO checkUploadToken(String token) { + String key = FileUploadCacheKeyDefine.FILE_UPLOAD.format(token); + // 查询缓存 + FileUploadTokenDTO info = RedisStrings.getJson(key, FileUploadCacheKeyDefine.FILE_UPLOAD); + if (info != null) { + // 删除缓存 + RedisStrings.delete(key); + } + return info; + } + +} diff --git a/orion-ops-ui/src/views/asset-audit/connect-session/components/connect-session-table.vue b/orion-ops-ui/src/views/asset-audit/connect-session/components/connect-session-table.vue index d4f78d94..e1bfaf2d 100644 --- a/orion-ops-ui/src/views/asset-audit/connect-session/components/connect-session-table.vue +++ b/orion-ops-ui/src/views/asset-audit/connect-session/components/connect-session-table.vue @@ -161,9 +161,11 @@ const forceOffline = async (record: HostConnectLogQueryResponse) => { try { setLoading(true); + // 下线 await hostForceOffline({ id: record.id }); - record.endTime = Date.now(); Message.success('已下线'); + // 移除行 + tableRenderData.value.splice(tableRenderData.value.findIndex(s => s.id === record.id), 1); } catch (e) { } finally { setLoading(false);