-
修改
@@ -184,7 +184,7 @@
setLoading(true);
// 调用删除接口
await batchDelete${vue.featureEntity}(selectedKeys.value);
- Message.success(`成功删除${selectedKeys.value.length}条数据`);
+ Message.success(`成功删除 ${selectedKeys.value.length} 条数据`);
selectedKeys.value = [];
// 重新加载数据
fetchTableData();
diff --git a/orion-ops-framework/orion-ops-spring-boot-starter-redis/src/main/java/com/orion/ops/framework/redis/core/utils/RedisUtils.java b/orion-ops-framework/orion-ops-spring-boot-starter-redis/src/main/java/com/orion/ops/framework/redis/core/utils/RedisUtils.java
index 83d05cf4..b6e28d80 100644
--- a/orion-ops-framework/orion-ops-spring-boot-starter-redis/src/main/java/com/orion/ops/framework/redis/core/utils/RedisUtils.java
+++ b/orion-ops-framework/orion-ops-spring-boot-starter-redis/src/main/java/com/orion/ops/framework/redis/core/utils/RedisUtils.java
@@ -10,6 +10,7 @@ import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import java.util.*;
+import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
@@ -165,6 +166,18 @@ public class RedisUtils {
}
}
+ /**
+ * 设置过期时间
+ *
+ * @param key key
+ * @param timeout timeout
+ * @param unit unit
+ */
+ public static void setExpire(String key, long timeout, TimeUnit unit) {
+ // 设置过期时间
+ redisTemplate.expire(key, timeout, unit);
+ }
+
public static void setRedisTemplate(RedisTemplate
redisTemplate) {
if (RedisUtils.redisTemplate != null) {
// unmodified
diff --git a/orion-ops-launch/src/main/resources/META-INF/spring-configuration-metadata.json b/orion-ops-launch/src/main/resources/META-INF/spring-configuration-metadata.json
new file mode 100644
index 00000000..a4099a50
--- /dev/null
+++ b/orion-ops-launch/src/main/resources/META-INF/spring-configuration-metadata.json
@@ -0,0 +1,36 @@
+{
+ "groups": [
+ {
+ "name": "app.authentication",
+ "type": "com.orion.ops.module.infra.config.AppAuthenticationConfig",
+ "sourceType": "com.orion.ops.module.infra.config.AppAuthenticationConfig"
+ }
+ ],
+ "properties": [
+ {
+ "name": "app.authentication.allowMultiDevice",
+ "type": "java.lang.Boolean",
+ "description": "是否允许多端登录."
+ },
+ {
+ "name": "app.authentication.allowRefresh",
+ "type": "java.lang.Boolean",
+ "description": "是否允许凭证续签."
+ },
+ {
+ "name": "app.authentication.maxRefreshCount",
+ "type": "java.lang.Integer",
+ "description": "凭证续签最大次数."
+ },
+ {
+ "name": "app.authentication.loginFailedLockCount",
+ "type": "java.lang.Integer",
+ "description": "登录失败锁定次数."
+ },
+ {
+ "name": "app.authentication.loginFailedLockTime",
+ "type": "java.lang.Integer",
+ "description": "登录失败锁定时间 (分)."
+ }
+ ]
+}
\ No newline at end of file
diff --git a/orion-ops-launch/src/main/resources/application.yaml b/orion-ops-launch/src/main/resources/application.yaml
index 21611fbd..11581757 100644
--- a/orion-ops-launch/src/main/resources/application.yaml
+++ b/orion-ops-launch/src/main/resources/application.yaml
@@ -118,6 +118,21 @@ logging:
level:
com.orion.ops.launch.controller.BootstrapController: INFO
+# 应用配置
+app:
+ authentication:
+ # 是否允许多端登录
+ allow-multi-device: true
+ # 是否允许凭证续签
+ allow-refresh: true
+ # 凭证续签最大次数
+ max-refresh-count: 3
+ # 登录失败锁定次数
+ login-failed-lock-count: 5
+ # 登录失败锁定时间 (分)
+ login-failed-lock-time: 30
+
+# orion framework config
orion:
# 版本
version: @revision@
@@ -151,7 +166,6 @@ orion:
asset:
group: "asset - 资产模块"
path: "asset"
-
logging:
# 全局日志打印
printer:
diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/controller/HostConnectLogController.java b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/controller/HostConnectLogController.java
index e91cdf28..97ea4623 100644
--- a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/controller/HostConnectLogController.java
+++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/controller/HostConnectLogController.java
@@ -1,22 +1,23 @@
package com.orion.ops.module.asset.controller;
import com.orion.lang.define.wrapper.DataGrid;
+import com.orion.ops.framework.biz.operator.log.core.annotation.OperatorLog;
+import com.orion.ops.framework.common.validator.group.Id;
import com.orion.ops.framework.common.validator.group.Page;
import com.orion.ops.framework.log.core.annotation.IgnoreLog;
import com.orion.ops.framework.log.core.enums.IgnoreLogMode;
import com.orion.ops.framework.web.core.annotation.RestWrapper;
+import com.orion.ops.module.asset.define.operator.HostConnectLogOperatorType;
import com.orion.ops.module.asset.entity.request.host.HostConnectLogQueryRequest;
import com.orion.ops.module.asset.entity.vo.HostConnectLogVO;
import com.orion.ops.module.asset.service.HostConnectLogService;
import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
@@ -55,5 +56,36 @@ public class HostConnectLogController {
return hostConnectLogService.getLatestConnectHostId(request);
}
+ @OperatorLog(HostConnectLogOperatorType.DELETE)
+ @DeleteMapping("/delete")
+ @Operation(summary = "删除主机连接日志")
+ @Parameter(name = "idList", description = "idList", required = true)
+ @PreAuthorize("@ss.hasPermission('asset:host-connect-log:management:delete')")
+ public Integer deleteHostConnectLog(@RequestParam("idList") List idList) {
+ return hostConnectLogService.deleteHostConnectLog(idList);
+ }
+
+ @PostMapping("/query-count")
+ @Operation(summary = "查询主机连接日志数量")
+ public Long getHostConnectLogCount(@RequestBody HostConnectLogQueryRequest request) {
+ return hostConnectLogService.getHostConnectLogCount(request);
+ }
+
+ @OperatorLog(HostConnectLogOperatorType.CLEAR)
+ @PostMapping("/clear")
+ @Operation(summary = "清空主机连接日志")
+ @PreAuthorize("@ss.hasPermission('asset:host-connect-log:management:clear')")
+ public Integer clearHostConnectLog(@RequestBody HostConnectLogQueryRequest request) {
+ return hostConnectLogService.clearHostConnectLog(request);
+ }
+
+ @OperatorLog(HostConnectLogOperatorType.FORCE_OFFLINE)
+ @PutMapping("/force-offline")
+ @Operation(summary = "强制断开主机连接")
+ @PreAuthorize("@ss.hasPermission('asset:host-connect-log:management:force-offline')")
+ public Integer forceOffline(@Validated(Id.class) @RequestBody HostConnectLogQueryRequest request) {
+ return hostConnectLogService.forceOffline(request);
+ }
+
}
diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/controller/HostSftpLogController.http b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/controller/HostSftpLogController.http
new file mode 100644
index 00000000..094101d8
--- /dev/null
+++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/controller/HostSftpLogController.http
@@ -0,0 +1,17 @@
+### 分页查询 SFTP 操作日志
+POST {{baseUrl}}/asset/host-sftp-log/query
+Content-Type: application/json
+Authorization: {{token}}
+
+{
+ "page": 1,
+ "limit": 10
+}
+
+
+### 删除 SFTP 操作日志
+DELETE {{baseUrl}}/asset/host-sftp-log/delete?idList=1,2,3
+Authorization: {{token}}
+
+
+###
diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/controller/HostSftpLogController.java b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/controller/HostSftpLogController.java
new file mode 100644
index 00000000..4c3c616c
--- /dev/null
+++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/controller/HostSftpLogController.java
@@ -0,0 +1,61 @@
+package com.orion.ops.module.asset.controller;
+
+import com.orion.lang.define.wrapper.DataGrid;
+import com.orion.ops.framework.biz.operator.log.core.annotation.OperatorLog;
+import com.orion.ops.framework.common.validator.group.Page;
+import com.orion.ops.framework.log.core.annotation.IgnoreLog;
+import com.orion.ops.framework.log.core.enums.IgnoreLogMode;
+import com.orion.ops.framework.web.core.annotation.RestWrapper;
+import com.orion.ops.module.asset.define.operator.HostTerminalOperatorType;
+import com.orion.ops.module.asset.entity.request.host.HostSftpLogQueryRequest;
+import com.orion.ops.module.asset.entity.vo.HostSftpLogVO;
+import com.orion.ops.module.asset.service.HostSftpLogService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import java.util.List;
+
+/**
+ * SFTP 操作日志服务 api
+ *
+ * @author Jiahang Li
+ * @version 1.0.0
+ * @since 2023-12-26 22:09
+ */
+@Tag(name = "asset - SFTP 操作日志服务")
+@Slf4j
+@Validated
+@RestWrapper
+@RestController
+@RequestMapping("/asset/host-sftp-log")
+@SuppressWarnings({"ELValidationInJSP", "SpringElInspection"})
+public class HostSftpLogController {
+
+ @Resource
+ private HostSftpLogService hostSftpLogService;
+
+ @IgnoreLog(IgnoreLogMode.RET)
+ @PostMapping("/query")
+ @Operation(summary = "分页查询 SFTP 操作日志")
+ @PreAuthorize("@ss.hasAnyPermission('infra:operator-log:query', 'asset:host-sftp-log:management:query')")
+ public DataGrid getHostSftpLogPage(@Validated(Page.class) @RequestBody HostSftpLogQueryRequest request) {
+ return hostSftpLogService.getHostSftpLogPage(request);
+ }
+
+ @OperatorLog(HostTerminalOperatorType.DELETE_SFTP_LOG)
+ @DeleteMapping("/delete")
+ @Operation(summary = "删除 SFTP 操作日志")
+ @Parameter(name = "idList", description = "idList", required = true)
+ @PreAuthorize("@ss.hasAnyPermission('infra:operator-log:delete', 'asset:host-sftp-log:management:delete')")
+ public Integer deleteHostSftpLog(@RequestParam("idList") List idList) {
+ return hostSftpLogService.deleteHostSftpLog(idList);
+ }
+
+}
+
diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/convert/HostSftpLogConvert.java b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/convert/HostSftpLogConvert.java
new file mode 100644
index 00000000..c340c1fe
--- /dev/null
+++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/convert/HostSftpLogConvert.java
@@ -0,0 +1,24 @@
+package com.orion.ops.module.asset.convert;
+
+import com.orion.ops.module.asset.entity.vo.HostSftpLogVO;
+import com.orion.ops.module.infra.entity.dto.operator.OperatorLogDTO;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.factory.Mappers;
+
+/**
+ * SFTP 操作日志 内部对象转换器
+ *
+ * @author Jiahang Li
+ * @version 1.0.0
+ * @since 2023-12-26 22:09
+ */
+@Mapper
+public interface HostSftpLogConvert {
+
+ HostSftpLogConvert MAPPER = Mappers.getMapper(HostSftpLogConvert.class);
+
+ @Mapping(target = "extra", ignore = true)
+ HostSftpLogVO to(OperatorLogDTO request);
+
+}
diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/define/operator/HostConnectLogOperatorType.java b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/define/operator/HostConnectLogOperatorType.java
new file mode 100644
index 00000000..e9b7f53e
--- /dev/null
+++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/define/operator/HostConnectLogOperatorType.java
@@ -0,0 +1,34 @@
+package com.orion.ops.module.asset.define.operator;
+
+import com.orion.ops.framework.biz.operator.log.core.annotation.Module;
+import com.orion.ops.framework.biz.operator.log.core.factory.InitializingOperatorTypes;
+import com.orion.ops.framework.biz.operator.log.core.model.OperatorType;
+
+import static com.orion.ops.framework.biz.operator.log.core.enums.OperatorRiskLevel.*;
+
+/**
+ * 主机连接日志 操作日志类型
+ *
+ * @author Jiahang Li
+ * @version 1.0.0
+ * @since 2024/3/2 14:37
+ */
+@Module("asset:host-connect-log")
+public class HostConnectLogOperatorType extends InitializingOperatorTypes {
+
+ public static final String DELETE = "host-connect-log:delete";
+
+ public static final String CLEAR = "host-connect-log:clear";
+
+ public static final String FORCE_OFFLINE = "host-connect-log:force-offline";
+
+ @Override
+ public OperatorType[] types() {
+ return new OperatorType[]{
+ new OperatorType(H, DELETE, "删除主机连接记录 ${count} 条"),
+ new OperatorType(H, CLEAR, "清空主机连接记录 ${count} 条"),
+ new OperatorType(M, FORCE_OFFLINE, "强制下线主机连接 ${hostName}"),
+ };
+ }
+
+}
diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/define/operator/HostTerminalOperatorType.java b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/define/operator/HostTerminalOperatorType.java
index 11ea7a86..4027b399 100644
--- a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/define/operator/HostTerminalOperatorType.java
+++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/define/operator/HostTerminalOperatorType.java
@@ -1,9 +1,12 @@
package com.orion.ops.module.asset.define.operator;
+import com.orion.lang.utils.collect.Lists;
import com.orion.ops.framework.biz.operator.log.core.annotation.Module;
import com.orion.ops.framework.biz.operator.log.core.factory.InitializingOperatorTypes;
import com.orion.ops.framework.biz.operator.log.core.model.OperatorType;
+import java.util.List;
+
import static com.orion.ops.framework.biz.operator.log.core.enums.OperatorRiskLevel.*;
/**
@@ -18,6 +21,8 @@ public class HostTerminalOperatorType extends InitializingOperatorTypes {
public static final String CONNECT = "host-terminal:connect";
+ public static final String DELETE_SFTP_LOG = "host-terminal:delete-sftp-log";
+
public static final String SFTP_MKDIR = "host-terminal:sftp-mkdir";
public static final String SFTP_TOUCH = "host-terminal:sftp-touch";
@@ -36,10 +41,23 @@ public class HostTerminalOperatorType extends InitializingOperatorTypes {
public static final String SFTP_DOWNLOAD = "host-terminal:sftp-download";
+ public static final List SFTP_TYPES = Lists.of(
+ SFTP_MKDIR,
+ SFTP_TOUCH,
+ SFTP_MOVE,
+ SFTP_REMOVE,
+ SFTP_TRUNCATE,
+ SFTP_CHMOD,
+ SFTP_SET_CONTENT,
+ SFTP_UPLOAD,
+ SFTP_DOWNLOAD
+ );
+
@Override
public OperatorType[] types() {
return new OperatorType[]{
new OperatorType(L, CONNECT, "连接主机 ${connectType} ${hostName}"),
+ new OperatorType(H, DELETE_SFTP_LOG, "删除 SFTP 操作日志 ${count} 条"),
new OperatorType(L, SFTP_MKDIR, "创建文件夹 ${hostName} ${path}"),
new OperatorType(L, SFTP_TOUCH, "创建文件 ${hostName} ${path}"),
new OperatorType(M, SFTP_MOVE, "移动文件 ${hostName} ${path} 至 ${target}"),
diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/entity/dto/HostConnectLogExtraDTO.java b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/entity/dto/HostConnectLogExtraDTO.java
new file mode 100644
index 00000000..b314e5cd
--- /dev/null
+++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/entity/dto/HostConnectLogExtraDTO.java
@@ -0,0 +1,53 @@
+package com.orion.ops.module.asset.entity.dto;
+
+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-3-12 23:31
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@Schema(name = "HostConnectLogExtraDTO", description = "主机连接日志推展信息对象")
+public class HostConnectLogExtraDTO {
+
+ @Schema(description = "hostId")
+ private Long hostId;
+
+ @Schema(description = "主机名称")
+ private String hostName;
+
+ @Schema(description = "连接类型")
+ private String connectType;
+
+ @Schema(description = "traceId")
+ private String traceId;
+
+ @Schema(description = "channelId")
+ private String channelId;
+
+ @Schema(description = "sessionId")
+ private String sessionId;
+
+ @Schema(description = "请求地址")
+ private String address;
+
+ @Schema(description = "请求位置")
+ private String location;
+
+ @Schema(description = "ua")
+ private String userAgent;
+
+ @Schema(description = "错误信息")
+ private String errorMessage;
+
+}
diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/entity/request/host/HostConnectLogQueryRequest.java b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/entity/request/host/HostConnectLogQueryRequest.java
index 75ec8373..9cb62906 100644
--- a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/entity/request/host/HostConnectLogQueryRequest.java
+++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/entity/request/host/HostConnectLogQueryRequest.java
@@ -23,6 +23,9 @@ import java.util.Date;
@Schema(name = "HostConnectLogQueryRequest", description = "主机连接日志 查询请求对象")
public class HostConnectLogQueryRequest extends PageRequest {
+ @Schema(description = "id")
+ private Long id;
+
@Schema(description = "用户id")
private Long userId;
diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/entity/request/host/HostSftpLogQueryRequest.java b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/entity/request/host/HostSftpLogQueryRequest.java
new file mode 100644
index 00000000..57b8cc8a
--- /dev/null
+++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/entity/request/host/HostSftpLogQueryRequest.java
@@ -0,0 +1,43 @@
+package com.orion.ops.module.asset.entity.request.host;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.orion.ops.framework.common.entity.PageRequest;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+
+import javax.validation.constraints.Size;
+import java.util.Date;
+
+/**
+ * SFTP 操作日志 查询请求对象
+ *
+ * @author Jiahang Li
+ * @version 1.0.0
+ * @since 2024-3-4 22:59
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@EqualsAndHashCode(callSuper = true)
+@Schema(name = "HostSftpLogQueryRequest", description = "SFTP 操作日志 查询请求对象")
+public class HostSftpLogQueryRequest extends PageRequest {
+
+ @Schema(description = "用户id")
+ private Long userId;
+
+ @Schema(description = "hostId")
+ private Long hostId;
+
+ @Size(max = 64)
+ @Schema(description = "操作类型")
+ private String type;
+
+ @Schema(description = "操作结果 0失败 1成功")
+ private Integer result;
+
+ @Schema(description = "开始时间-区间")
+ @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+ private Date[] startTimeRange;
+
+}
diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/entity/vo/HostConnectLogVO.java b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/entity/vo/HostConnectLogVO.java
index f09caf65..58762711 100644
--- a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/entity/vo/HostConnectLogVO.java
+++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/entity/vo/HostConnectLogVO.java
@@ -1,5 +1,6 @@
package com.orion.ops.module.asset.entity.vo;
+import com.orion.ops.module.asset.entity.dto.HostConnectLogExtraDTO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
@@ -46,9 +47,6 @@ public class HostConnectLogVO implements Serializable {
@Schema(description = "类型")
private String type;
- @Schema(description = "token")
- private String token;
-
@Schema(description = "状态")
private String status;
@@ -59,18 +57,6 @@ public class HostConnectLogVO implements Serializable {
private Date endTime;
@Schema(description = "额外信息")
- private String extraInfo;
-
- @Schema(description = "创建时间")
- private Date createTime;
-
- @Schema(description = "修改时间")
- private Date updateTime;
-
- @Schema(description = "创建人")
- private String creator;
-
- @Schema(description = "修改人")
- private String updater;
+ private HostConnectLogExtraDTO extra;
}
diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/entity/vo/HostSftpLogVO.java b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/entity/vo/HostSftpLogVO.java
new file mode 100644
index 00000000..5a8d812d
--- /dev/null
+++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/entity/vo/HostSftpLogVO.java
@@ -0,0 +1,71 @@
+package com.orion.ops.module.asset.entity.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.Map;
+
+/**
+ * SFTP 操作日志 实体对象
+ *
+ * @author Jiahang Li
+ * @version 1.0.0
+ * @since 2023-10-10 17:08
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@Schema(name = "HostSftpLogVO", description = "SFTP 操作日志 实体对象")
+public class HostSftpLogVO implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "id")
+ private Long id;
+
+ @Schema(description = "用户id")
+ private Long userId;
+
+ @Schema(description = "用户名")
+ private String username;
+
+ @Schema(description = "主机id")
+ private Long hostId;
+
+ @Schema(description = "主机名称")
+ private String hostName;
+
+ @Schema(description = "主机地址")
+ private String hostAddress;
+
+ @Schema(description = "操作文件")
+ private String[] paths;
+
+ @Schema(description = "请求ip")
+ private String address;
+
+ @Schema(description = "请求地址")
+ private String location;
+
+ @Schema(description = "userAgent")
+ private String userAgent;
+
+ @Schema(description = "操作类型")
+ private String type;
+
+ @Schema(description = "参数")
+ private Map extra;
+
+ @Schema(description = "操作结果 0失败 1成功")
+ private Integer result;
+
+ @Schema(description = "开始时间")
+ private Date startTime;
+
+}
diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/enums/HostConnectStatusEnum.java b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/enums/HostConnectStatusEnum.java
index 546df6fb..35706cb8 100644
--- a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/enums/HostConnectStatusEnum.java
+++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/enums/HostConnectStatusEnum.java
@@ -24,6 +24,11 @@ public enum HostConnectStatusEnum {
*/
FAILED,
+ /**
+ * 强制下线
+ */
+ FORCE_OFFLINE,
+
;
public static HostConnectStatusEnum of(String type) {
diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/terminal/enums/OutputTypeEnum.java b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/terminal/enums/OutputTypeEnum.java
index 9c414fcc..947af678 100644
--- a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/terminal/enums/OutputTypeEnum.java
+++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/terminal/enums/OutputTypeEnum.java
@@ -28,7 +28,7 @@ public enum OutputTypeEnum {
/**
* 关闭连接
*/
- CLOSE("cl", "${type}|${sessionId}|${msg}"),
+ CLOSE("cl", "${type}|${sessionId}|${forceClose}|${msg}"),
/**
* pong
diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/terminal/handler/SftpRemoveHandler.java b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/terminal/handler/SftpRemoveHandler.java
index 5e9a603b..8ed4b629 100644
--- a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/terminal/handler/SftpRemoveHandler.java
+++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/terminal/handler/SftpRemoveHandler.java
@@ -2,7 +2,6 @@ package com.orion.ops.module.asset.handler.host.terminal.handler;
import com.orion.lang.utils.collect.Maps;
import com.orion.ops.framework.biz.operator.log.core.utils.OperatorLogs;
-import com.orion.ops.framework.common.constant.Const;
import com.orion.ops.framework.common.enums.BooleanBit;
import com.orion.ops.module.asset.define.operator.HostTerminalOperatorType;
import com.orion.ops.module.asset.handler.host.terminal.enums.OutputTypeEnum;
@@ -54,7 +53,7 @@ public class SftpRemoveHandler extends AbstractTerminalHandler
.build());
// 保存操作日志
Map extra = Maps.newMap();
- extra.put(OperatorLogs.PATH, String.join(Const.COMMA, paths));
+ extra.put(OperatorLogs.PATH, payload.getPath());
this.saveOperatorLog(payload, channel,
extra, HostTerminalOperatorType.SFTP_REMOVE,
startTime, ex);
diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/terminal/handler/TerminalCheckHandler.java b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/terminal/handler/TerminalCheckHandler.java
index 3950c022..c2b23247 100644
--- a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/terminal/handler/TerminalCheckHandler.java
+++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/terminal/handler/TerminalCheckHandler.java
@@ -173,7 +173,7 @@ public class TerminalCheckHandler extends AbstractTerminalHandler extra = Maps.newMap();
- extra.put(OperatorLogs.ID, hostId);
+ extra.put(OperatorLogs.HOST_ID, hostId);
extra.put(OperatorLogs.HOST_NAME, hostName);
extra.put(OperatorLogs.CONNECT_TYPE, connectType.name());
extra.put(OperatorLogs.CHANNEL_ID, channel.getId());
@@ -194,6 +194,13 @@ public class TerminalCheckHandler extends AbstractTerminalHandler extra = Maps.newMap(4);
+ extra.put(ExtraFieldConst.ERROR_MESSAGE, this.getConnectErrorMessage(e));
+ hostConnectLogService.updateStatusByToken(sessionId, HostConnectStatusEnum.FAILED, extra);
}
// 返回连接状态
this.send(channel,
diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/terminal/model/response/TerminalCloseResponse.java b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/terminal/model/response/TerminalCloseResponse.java
index a6be8ae9..7b720c7b 100644
--- a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/terminal/model/response/TerminalCloseResponse.java
+++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/terminal/model/response/TerminalCloseResponse.java
@@ -23,6 +23,9 @@ import lombok.experimental.SuperBuilder;
@Schema(name = "TerminalCloseResponse", description = "主机连接关闭响应 实体对象")
public class TerminalCloseResponse extends TerminalBasePayload {
+ @Schema(description = "是否为强制关闭")
+ private Integer forceClose;
+
@Schema(description = "关闭信息")
private String msg;
diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/terminal/session/ITerminalSession.java b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/terminal/session/ITerminalSession.java
index 26efb106..211bbd4b 100644
--- a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/terminal/session/ITerminalSession.java
+++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/terminal/session/ITerminalSession.java
@@ -38,4 +38,9 @@ public interface ITerminalSession extends SafeCloseable {
*/
void keepAlive();
+ /**
+ * 强制下线
+ */
+ void forceOffline();
+
}
diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/terminal/session/SshSession.java b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/terminal/session/SshSession.java
index 88b78403..19ff33e2 100644
--- a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/terminal/session/SshSession.java
+++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/terminal/session/SshSession.java
@@ -4,6 +4,7 @@ import com.orion.lang.utils.io.Streams;
import com.orion.net.host.SessionStore;
import com.orion.net.host.ssh.shell.ShellExecutor;
import com.orion.ops.framework.common.constant.Const;
+import com.orion.ops.framework.common.enums.BooleanBit;
import com.orion.ops.framework.websocket.core.utils.WebSockets;
import com.orion.ops.module.asset.define.AssetThreadPools;
import com.orion.ops.module.asset.handler.host.terminal.constant.TerminalMessage;
@@ -53,7 +54,7 @@ public class SshSession extends TerminalSession implements ISshSession {
executor.size(cols, rows);
executor.terminalType(terminalType);
executor.streamHandler(this::streamHandler);
- executor.callback(this::eofCallback);
+ executor.callback(this::close);
executor.connect();
// 开始监听输出
AssetThreadPools.TERMINAL_STDOUT.execute(executor);
@@ -122,20 +123,4 @@ public class SshSession extends TerminalSession implements ISshSession {
}
}
- /**
- * eof 回调
- */
- private void eofCallback() {
- log.info("terminal eof回调 {}, forClose: {}", sessionId, this.close);
- // 发送关闭信息
- TerminalCloseResponse resp = TerminalCloseResponse.builder()
- .type(OutputTypeEnum.CLOSE.getType())
- .sessionId(this.sessionId)
- .msg(TerminalMessage.CLOSED_CONNECTION)
- .build();
- WebSockets.sendText(channel, OutputTypeEnum.CLOSE.format(resp));
- // 需要调用关闭 - 可能是 logout 需要手动触发
- this.close();
- }
-
}
diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/terminal/session/TerminalSession.java b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/terminal/session/TerminalSession.java
index 054d358a..82192308 100644
--- a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/terminal/session/TerminalSession.java
+++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/terminal/session/TerminalSession.java
@@ -1,7 +1,12 @@
package com.orion.ops.module.asset.handler.host.terminal.session;
+import com.orion.ops.framework.common.enums.BooleanBit;
+import com.orion.ops.framework.websocket.core.utils.WebSockets;
import com.orion.ops.module.asset.enums.HostConnectStatusEnum;
+import com.orion.ops.module.asset.handler.host.terminal.constant.TerminalMessage;
+import com.orion.ops.module.asset.handler.host.terminal.enums.OutputTypeEnum;
import com.orion.ops.module.asset.handler.host.terminal.model.TerminalConfig;
+import com.orion.ops.module.asset.handler.host.terminal.model.response.TerminalCloseResponse;
import com.orion.ops.module.asset.service.HostConnectLogService;
import com.orion.spring.SpringHolder;
import lombok.Getter;
@@ -28,6 +33,8 @@ public abstract class TerminalSession implements ITerminalSession {
protected volatile boolean close;
+ protected volatile boolean forceOffline;
+
public TerminalSession(String sessionId, WebSocketSession channel, TerminalConfig config) {
this.sessionId = sessionId;
this.channel = channel;
@@ -39,11 +46,48 @@ public abstract class TerminalSession implements ITerminalSession {
*/
protected abstract void releaseResource();
+ /**
+ * 发送关闭消息
+ */
+ protected void sendCloseMessage() {
+ log.info("TerminalSession close {}, forClose: {}, forceOffline: {}", sessionId, this.close, this.forceOffline);
+ // 发送关闭信息
+ TerminalCloseResponse resp = TerminalCloseResponse.builder()
+ .type(OutputTypeEnum.CLOSE.getType())
+ .sessionId(this.sessionId)
+ .forceClose(BooleanBit.of(this.forceOffline).getValue())
+ .msg(this.forceOffline ? TerminalMessage.FORCED_OFFLINE : TerminalMessage.CLOSED_CONNECTION)
+ .build();
+ WebSockets.sendText(channel, OutputTypeEnum.CLOSE.format(resp));
+ }
+
@Override
public void close() {
log.info("terminal close {}", sessionId);
+ // 检查并且关闭
+ if (this.checkAndClose()) {
+ // 修改状态
+ SpringHolder.getBean(HostConnectLogService.class)
+ .updateStatusByToken(sessionId, HostConnectStatusEnum.COMPLETE, null);
+ }
+ }
+
+ @Override
+ public void forceOffline() {
+ log.info("terminal forceOffline {}", sessionId);
+ this.forceOffline = true;
+ // 关闭
+ this.checkAndClose();
+ }
+
+ /**
+ * 检查并且关闭会话
+ *
+ * @return close
+ */
+ private boolean checkAndClose() {
if (close) {
- return;
+ return false;
}
this.close = true;
// 释放资源
@@ -52,8 +96,13 @@ public abstract class TerminalSession implements ITerminalSession {
} catch (Exception e) {
log.error("terminal release error {}", sessionId, e);
}
- // 修改状态
- SpringHolder.getBean(HostConnectLogService.class).updateStatusByToken(sessionId, HostConnectStatusEnum.COMPLETE);
+ // 发送关闭信息
+ try {
+ this.sendCloseMessage();
+ } catch (Exception e) {
+ log.error("terminal send close error {}", sessionId, e);
+ }
+ return true;
}
@Override
diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/service/HostConnectLogService.java b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/service/HostConnectLogService.java
index 85a08145..e1e34585 100644
--- a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/service/HostConnectLogService.java
+++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/service/HostConnectLogService.java
@@ -8,6 +8,7 @@ import com.orion.ops.module.asset.enums.HostConnectStatusEnum;
import com.orion.ops.module.asset.enums.HostConnectTypeEnum;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.Future;
/**
@@ -40,8 +41,10 @@ public interface HostConnectLogService {
*
* @param token token
* @param status status
+ * @param extra extra
+ * @return effect
*/
- void updateStatusByToken(String token, HostConnectStatusEnum status);
+ Integer updateStatusByToken(String token, HostConnectStatusEnum status, Map extra);
/**
* 查询用户最近连接的主机
@@ -60,4 +63,36 @@ public interface HostConnectLogService {
*/
Future> getLatestConnectHostIdAsync(HostConnectTypeEnum type, Long userId);
+ /**
+ * 删除主机连接日志
+ *
+ * @param idList idList
+ * @return effect
+ */
+ Integer deleteHostConnectLog(List idList);
+
+ /**
+ * 获取主机连接日志数量
+ *
+ * @param request request
+ * @return count
+ */
+ Long getHostConnectLogCount(HostConnectLogQueryRequest request);
+
+ /**
+ * 清空主机连接日志
+ *
+ * @param request request
+ * @return effect
+ */
+ Integer clearHostConnectLog(HostConnectLogQueryRequest request);
+
+ /**
+ * 强制断开主机连接
+ *
+ * @param request request
+ * @return effect
+ */
+ Integer forceOffline(HostConnectLogQueryRequest request);
+
}
diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/service/HostSftpLogService.java b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/service/HostSftpLogService.java
new file mode 100644
index 00000000..8049d82f
--- /dev/null
+++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/service/HostSftpLogService.java
@@ -0,0 +1,34 @@
+package com.orion.ops.module.asset.service;
+
+import com.orion.lang.define.wrapper.DataGrid;
+import com.orion.ops.module.asset.entity.request.host.HostSftpLogQueryRequest;
+import com.orion.ops.module.asset.entity.vo.HostSftpLogVO;
+
+import java.util.List;
+
+/**
+ * SFTP 操作日志 服务类
+ *
+ * @author Jiahang Li
+ * @version 1.0.0
+ * @since 2023-12-26 22:09
+ */
+public interface HostSftpLogService {
+
+ /**
+ * 分页查询 SFTP 操作日志
+ *
+ * @param request request
+ * @return rows
+ */
+ DataGrid getHostSftpLogPage(HostSftpLogQueryRequest request);
+
+ /**
+ * 删除 SFTP 操作日志
+ *
+ * @param idList idList
+ * @return effect
+ */
+ Integer deleteHostSftpLog(List idList);
+
+}
diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/service/impl/HostConnectLogServiceImpl.java b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/service/impl/HostConnectLogServiceImpl.java
index e6a1cfea..e4195810 100644
--- a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/service/impl/HostConnectLogServiceImpl.java
+++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/service/impl/HostConnectLogServiceImpl.java
@@ -5,16 +5,21 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.orion.lang.constant.Const;
import com.orion.lang.define.wrapper.DataGrid;
import com.orion.lang.utils.Arrays1;
-import com.orion.ops.framework.mybatis.core.query.Conditions;
+import com.orion.lang.utils.Valid;
+import com.orion.ops.framework.biz.operator.log.core.utils.OperatorLogs;
+import com.orion.ops.framework.common.constant.ErrorMessage;
import com.orion.ops.framework.security.core.utils.SecurityUtils;
import com.orion.ops.module.asset.convert.HostConnectLogConvert;
import com.orion.ops.module.asset.dao.HostConnectLogDAO;
import com.orion.ops.module.asset.entity.domain.HostConnectLogDO;
+import com.orion.ops.module.asset.entity.dto.HostConnectLogExtraDTO;
import com.orion.ops.module.asset.entity.request.host.HostConnectLogCreateRequest;
import com.orion.ops.module.asset.entity.request.host.HostConnectLogQueryRequest;
import com.orion.ops.module.asset.entity.vo.HostConnectLogVO;
import com.orion.ops.module.asset.enums.HostConnectStatusEnum;
import com.orion.ops.module.asset.enums.HostConnectTypeEnum;
+import com.orion.ops.module.asset.handler.host.terminal.manager.TerminalManager;
+import com.orion.ops.module.asset.handler.host.terminal.session.ITerminalSession;
import com.orion.ops.module.asset.service.HostConnectLogService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
@@ -23,6 +28,7 @@ import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;
@@ -40,6 +46,9 @@ public class HostConnectLogServiceImpl implements HostConnectLogService {
@Resource
private HostConnectLogDAO hostConnectLogDAO;
+ @Resource
+ private TerminalManager terminalManager;
+
@Override
public void create(HostConnectTypeEnum type, HostConnectLogCreateRequest request) {
HostConnectLogDO record = HostConnectLogConvert.MAPPER.to(request);
@@ -62,16 +71,54 @@ public class HostConnectLogServiceImpl implements HostConnectLogService {
// 查询
return hostConnectLogDAO.of(wrapper)
.page(request)
- .dataGrid(HostConnectLogConvert.MAPPER::to);
+ .dataGrid(s -> {
+ HostConnectLogVO vo = HostConnectLogConvert.MAPPER.to(s);
+ vo.setExtra(JSON.parseObject(s.getExtraInfo(), HostConnectLogExtraDTO.class));
+ return vo;
+ });
}
@Override
- public void updateStatusByToken(String token, HostConnectStatusEnum status) {
- log.info("HostConnectLogService-updateStatusByToken token: {}, status: {}", token, status);
+ public Integer updateStatusByToken(String token, HostConnectStatusEnum status, Map partial) {
+ log.info("HostConnectLogService-updateStatusByToken start token: {}, status: {}", token, status);
+ // 查询
+ HostConnectLogDO record = hostConnectLogDAO.of()
+ .createWrapper()
+ .eq(HostConnectLogDO::getToken, token)
+ .orderByDesc(HostConnectLogDO::getId)
+ .then()
+ .getOne();
+ if (record == null) {
+ log.info("HostConnectLogService-updateStatusByToken no record token: {}", token);
+ return Const.N_0;
+ }
+ return this.updateStatus(record, status, partial);
+ }
+
+ /**
+ * 更新状态
+ *
+ * @param record record
+ * @param status status
+ * @param partial partial
+ * @return effect
+ */
+ private int updateStatus(HostConnectLogDO record, HostConnectStatusEnum status, Map partial) {
+ // 更新
HostConnectLogDO update = new HostConnectLogDO();
+ update.setId(record.getId());
update.setStatus(status.name());
update.setEndTime(new Date());
- hostConnectLogDAO.update(update, Conditions.eq(HostConnectLogDO::getToken, token));
+ if (partial != null) {
+ Map extra = JSON.parseObject(record.getExtraInfo());
+ if (extra == null) {
+ extra = partial;
+ } else {
+ extra.putAll(partial);
+ }
+ update.setExtraInfo(JSON.toJSONString(extra));
+ }
+ return hostConnectLogDAO.updateById(update);
}
@Override
@@ -86,6 +133,53 @@ public class HostConnectLogServiceImpl implements HostConnectLogService {
return CompletableFuture.completedFuture(hostIdList);
}
+ @Override
+ public Integer deleteHostConnectLog(List idList) {
+ log.info("HostConnectLogService.deleteHostConnectLog start {}", JSON.toJSONString(idList));
+ int effect = hostConnectLogDAO.deleteBatchIds(idList);
+ log.info("HostConnectLogService.deleteHostConnectLog finish {}", effect);
+ // 设置日志参数
+ OperatorLogs.add(OperatorLogs.COUNT, effect);
+ return effect;
+ }
+
+ @Override
+ public Long getHostConnectLogCount(HostConnectLogQueryRequest request) {
+ return hostConnectLogDAO.selectCount(this.buildQueryWrapper(request));
+ }
+
+ @Override
+ public Integer clearHostConnectLog(HostConnectLogQueryRequest request) {
+ log.info("HostConnectLogService.clearHostConnectLog start {}", JSON.toJSONString(request));
+ // 删除
+ LambdaQueryWrapper wrapper = this.buildQueryWrapper(request);
+ int effect = hostConnectLogDAO.delete(wrapper);
+ log.info("HostConnectLogService.clearHostConnectLog finish {}", effect);
+ // 设置日志参数
+ OperatorLogs.add(OperatorLogs.COUNT, effect);
+ return effect;
+ }
+
+ @Override
+ public Integer forceOffline(HostConnectLogQueryRequest request) {
+ Long id = request.getId();
+ // 查询数据是否存在
+ HostConnectLogDO record = hostConnectLogDAO.selectById(id);
+ Valid.notNull(record, ErrorMessage.LOG_ABSENT);
+ Valid.eq(record.getStatus(), HostConnectStatusEnum.CONNECTING.name(), ErrorMessage.ILLEGAL_STATUS);
+ // 设置日志参数
+ OperatorLogs.add(OperatorLogs.HOST_NAME, record.getHostName());
+ // 获取会话
+ HostConnectLogExtraDTO extra = JSON.parseObject(record.getExtraInfo(), HostConnectLogExtraDTO.class);
+ ITerminalSession session = terminalManager.getSession(extra.getChannelId(), extra.getSessionId());
+ if (session != null) {
+ // 关闭会话
+ session.forceOffline();
+ }
+ // 更新状态
+ return this.updateStatus(record, HostConnectStatusEnum.FORCE_OFFLINE, null);
+ }
+
/**
* 构建查询 wrapper
*
@@ -94,6 +188,7 @@ public class HostConnectLogServiceImpl implements HostConnectLogService {
*/
private LambdaQueryWrapper buildQueryWrapper(HostConnectLogQueryRequest request) {
return hostConnectLogDAO.wrapper()
+ .eq(HostConnectLogDO::getId, request.getId())
.eq(HostConnectLogDO::getUserId, request.getUserId())
.eq(HostConnectLogDO::getHostId, request.getHostId())
.like(HostConnectLogDO::getHostAddress, request.getHostAddress())
diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/service/impl/HostSftpLogServiceImpl.java b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/service/impl/HostSftpLogServiceImpl.java
new file mode 100644
index 00000000..0d7c376c
--- /dev/null
+++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/service/impl/HostSftpLogServiceImpl.java
@@ -0,0 +1,107 @@
+package com.orion.ops.module.asset.service.impl;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.orion.lang.define.wrapper.DataGrid;
+import com.orion.lang.utils.Arrays1;
+import com.orion.lang.utils.Strings;
+import com.orion.ops.framework.biz.operator.log.core.utils.OperatorLogs;
+import com.orion.ops.framework.common.constant.ExtraFieldConst;
+import com.orion.ops.module.asset.convert.HostSftpLogConvert;
+import com.orion.ops.module.asset.define.operator.HostTerminalOperatorType;
+import com.orion.ops.module.asset.entity.request.host.HostSftpLogQueryRequest;
+import com.orion.ops.module.asset.entity.vo.HostSftpLogVO;
+import com.orion.ops.module.asset.service.HostSftpLogService;
+import com.orion.ops.module.infra.api.OperatorLogApi;
+import com.orion.ops.module.infra.entity.dto.operator.OperatorLogDTO;
+import com.orion.ops.module.infra.entity.dto.operator.OperatorLogQueryDTO;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * SFTP 操作日志 服务实现类
+ *
+ * @author Jiahang Li
+ * @version 1.0.0
+ * @since 2024/3/4 23:35
+ */
+@Slf4j
+@Service
+public class HostSftpLogServiceImpl implements HostSftpLogService {
+
+ @Resource
+ private OperatorLogApi operatorLogApi;
+
+ @Override
+ public DataGrid getHostSftpLogPage(HostSftpLogQueryRequest request) {
+ // 查询
+ OperatorLogQueryDTO query = this.buildQueryInfo(request);
+ DataGrid dataGrid = operatorLogApi.getOperatorLogPage(query);
+ // 转换
+ List rows = dataGrid.stream()
+ .map(s -> {
+ JSONObject extra = JSON.parseObject(s.getExtra());
+ HostSftpLogVO vo = HostSftpLogConvert.MAPPER.to(s);
+ vo.setHostId(extra.getLong(ExtraFieldConst.HOST_ID));
+ vo.setHostName(extra.getString(ExtraFieldConst.HOST_NAME));
+ vo.setHostAddress(extra.getString(ExtraFieldConst.ADDRESS));
+ vo.setPaths(extra.getString(ExtraFieldConst.PATH).split("\\|"));
+ vo.setExtra(extra);
+ return vo;
+ }).collect(Collectors.toList());
+ // 返回
+ DataGrid result = new DataGrid<>();
+ result.setRows(rows);
+ result.setPage(dataGrid.getPage());
+ result.setLimit(dataGrid.getLimit());
+ result.setSize(dataGrid.getSize());
+ result.setTotal(dataGrid.getTotal());
+ return result;
+ }
+
+ @Override
+ public Integer deleteHostSftpLog(List idList) {
+ log.info("HostSftpLogService.deleteSftpLog start {}", JSON.toJSONString(idList));
+ Integer effect = operatorLogApi.deleteOperatorLog(idList);
+ log.info("HostSftpLogService.deleteSftpLog finish {}", effect);
+ // 设置日志参数
+ OperatorLogs.add(OperatorLogs.COUNT, effect);
+ return effect;
+ }
+
+ /**
+ * 构建查询对象
+ *
+ * @param request request
+ * @return query
+ */
+ private OperatorLogQueryDTO buildQueryInfo(HostSftpLogQueryRequest request) {
+ Long hostId = request.getHostId();
+ String type = request.getType();
+ // 构建参数
+ OperatorLogQueryDTO query = OperatorLogQueryDTO.builder()
+ .userId(request.getUserId())
+ .result(request.getResult())
+ .startTimeStart(Arrays1.getIfPresent(request.getStartTimeRange(), 0))
+ .startTimeEnd(Arrays1.getIfPresent(request.getStartTimeRange(), 1))
+ .build();
+ query.setPage(request.getPage());
+ query.setLimit(request.getLimit());
+ if (Strings.isBlank(type)) {
+ // 查询全部 SFTP 类型
+ query.setTypeList(HostTerminalOperatorType.SFTP_TYPES);
+ } else {
+ query.setType(type);
+ }
+ // 模糊查询
+ if (hostId != null) {
+ query.setExtra("\"hostId\": " + hostId + ",");
+ }
+ return query;
+ }
+
+}
diff --git a/orion-ops-module-infra/orion-ops-module-infra-provider/src/main/java/com/orion/ops/module/infra/api/OperatorLogApi.java b/orion-ops-module-infra/orion-ops-module-infra-provider/src/main/java/com/orion/ops/module/infra/api/OperatorLogApi.java
new file mode 100644
index 00000000..34d16935
--- /dev/null
+++ b/orion-ops-module-infra/orion-ops-module-infra-provider/src/main/java/com/orion/ops/module/infra/api/OperatorLogApi.java
@@ -0,0 +1,34 @@
+package com.orion.ops.module.infra.api;
+
+import com.orion.lang.define.wrapper.DataGrid;
+import com.orion.ops.module.infra.entity.dto.operator.OperatorLogDTO;
+import com.orion.ops.module.infra.entity.dto.operator.OperatorLogQueryDTO;
+
+import java.util.List;
+
+/**
+ * 操作日志服务
+ *
+ * @author Jiahang Li
+ * @version 1.0.0
+ * @since 2024/3/4 23:11
+ */
+public interface OperatorLogApi {
+
+ /**
+ * 分页查询操作日志
+ *
+ * @param request request
+ * @return rows
+ */
+ DataGrid getOperatorLogPage(OperatorLogQueryDTO request);
+
+ /**
+ * 删除操作日志
+ *
+ * @param idList idList
+ * @return effect
+ */
+ Integer deleteOperatorLog(List idList);
+
+}
diff --git a/orion-ops-module-infra/orion-ops-module-infra-provider/src/main/java/com/orion/ops/module/infra/entity/dto/operator/OperatorLogDTO.java b/orion-ops-module-infra/orion-ops-module-infra-provider/src/main/java/com/orion/ops/module/infra/entity/dto/operator/OperatorLogDTO.java
new file mode 100644
index 00000000..bcbe4db8
--- /dev/null
+++ b/orion-ops-module-infra/orion-ops-module-infra-provider/src/main/java/com/orion/ops/module/infra/entity/dto/operator/OperatorLogDTO.java
@@ -0,0 +1,85 @@
+package com.orion.ops.module.infra.entity.dto.operator;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 操作日志 业务对象
+ *
+ * @author Jiahang Li
+ * @version 1.0.0
+ * @since 2023-10-10 17:08
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@Schema(name = "OperatorLogDTO", description = "操作日志 业务对象")
+public class OperatorLogDTO implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "id")
+ private Long id;
+
+ @Schema(description = "用户id")
+ private Long userId;
+
+ @Schema(description = "用户名")
+ private String username;
+
+ @Schema(description = "traceId")
+ private String traceId;
+
+ @Schema(description = "请求ip")
+ private String address;
+
+ @Schema(description = "请求地址")
+ private String location;
+
+ @Schema(description = "userAgent")
+ private String userAgent;
+
+ @Schema(description = "风险等级")
+ private String riskLevel;
+
+ @Schema(description = "模块")
+ private String module;
+
+ @Schema(description = "操作类型")
+ private String type;
+
+ @Schema(description = "日志")
+ private String logInfo;
+
+ @Schema(description = "参数")
+ private String extra;
+
+ @Schema(description = "操作结果 0失败 1成功")
+ private Integer result;
+
+ @Schema(description = "错误信息")
+ private String errorMessage;
+
+ @Schema(description = "返回值")
+ private String returnValue;
+
+ @Schema(description = "操作时间")
+ private Integer duration;
+
+ @Schema(description = "开始时间")
+ private Date startTime;
+
+ @Schema(description = "结束时间")
+ private Date endTime;
+
+ @Schema(description = "创建时间")
+ private Date createTime;
+
+}
diff --git a/orion-ops-module-infra/orion-ops-module-infra-provider/src/main/java/com/orion/ops/module/infra/entity/dto/operator/OperatorLogQueryDTO.java b/orion-ops-module-infra/orion-ops-module-infra-provider/src/main/java/com/orion/ops/module/infra/entity/dto/operator/OperatorLogQueryDTO.java
new file mode 100644
index 00000000..57af39b8
--- /dev/null
+++ b/orion-ops-module-infra/orion-ops-module-infra-provider/src/main/java/com/orion/ops/module/infra/entity/dto/operator/OperatorLogQueryDTO.java
@@ -0,0 +1,61 @@
+package com.orion.ops.module.infra.entity.dto.operator;
+
+import com.orion.ops.framework.common.entity.PageRequest;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+
+import javax.validation.constraints.Size;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 操作日志 查询对象
+ *
+ * @author Jiahang Li
+ * @version 1.0.0
+ * @since 2023-10-10 17:08
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@EqualsAndHashCode(callSuper = true)
+@Schema(name = "OperatorLogQueryDTO", description = "操作日志 查询对象")
+public class OperatorLogQueryDTO extends PageRequest {
+
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "id")
+ private Long id;
+
+ @Schema(description = "用户id")
+ private Long userId;
+
+ @Size(max = 32)
+ @Schema(description = "模块")
+ private String module;
+
+ @Size(max = 1)
+ @Schema(description = "风险等级")
+ private String riskLevel;
+
+ @Size(max = 64)
+ @Schema(description = "操作类型")
+ private String type;
+
+ @Schema(description = "操作类型")
+ private List typeList;
+
+ @Schema(description = "参数")
+ private String extra;
+
+ @Schema(description = "操作结果 0失败 1成功")
+ private Integer result;
+
+ @Schema(description = "开始时间区间 - 开始")
+ private Date startTimeStart;
+
+ @Schema(description = "开始时间区间 - 结束")
+ private Date startTimeEnd;
+
+}
diff --git a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/api/impl/OperatorLogApiImpl.java b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/api/impl/OperatorLogApiImpl.java
new file mode 100644
index 00000000..3975301f
--- /dev/null
+++ b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/api/impl/OperatorLogApiImpl.java
@@ -0,0 +1,67 @@
+package com.orion.ops.module.infra.api.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.orion.lang.define.wrapper.DataGrid;
+import com.orion.ops.framework.common.utils.Valid;
+import com.orion.ops.module.infra.api.OperatorLogApi;
+import com.orion.ops.module.infra.convert.OperatorLogProviderConvert;
+import com.orion.ops.module.infra.dao.OperatorLogDAO;
+import com.orion.ops.module.infra.entity.domain.OperatorLogDO;
+import com.orion.ops.module.infra.entity.dto.operator.OperatorLogDTO;
+import com.orion.ops.module.infra.entity.dto.operator.OperatorLogQueryDTO;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.util.List;
+
+/**
+ * 操作日志服务实现
+ *
+ * @author Jiahang Li
+ * @version 1.0.0
+ * @since 2024/3/4 23:18
+ */
+@Slf4j
+@Service
+public class OperatorLogApiImpl implements OperatorLogApi {
+
+ @Resource
+ private OperatorLogDAO operatorLogDAO;
+
+ @Override
+ public DataGrid getOperatorLogPage(OperatorLogQueryDTO request) {
+ Valid.valid(request);
+ return operatorLogDAO.of()
+ .page(request)
+ .wrapper(this.buildQueryWrapper(request))
+ .dataGrid(OperatorLogProviderConvert.MAPPER::to);
+ }
+
+ @Override
+ public Integer deleteOperatorLog(List idList) {
+ return operatorLogDAO.deleteBatchIds(idList);
+ }
+
+ /**
+ * 构建查询 wrapper
+ *
+ * @param request request
+ * @return wrapper
+ */
+ private LambdaQueryWrapper buildQueryWrapper(OperatorLogQueryDTO request) {
+ return operatorLogDAO.wrapper()
+ .eq(OperatorLogDO::getId, request.getId())
+ .eq(OperatorLogDO::getUserId, request.getUserId())
+ .eq(OperatorLogDO::getRiskLevel, request.getRiskLevel())
+ .eq(OperatorLogDO::getModule, request.getModule())
+ .eq(OperatorLogDO::getType, request.getType())
+ .in(OperatorLogDO::getType, request.getTypeList())
+ .eq(OperatorLogDO::getResult, request.getResult())
+ .like(OperatorLogDO::getExtra, request.getExtra())
+ .ge(OperatorLogDO::getStartTime, request.getStartTimeStart())
+ .le(OperatorLogDO::getStartTime, request.getStartTimeEnd())
+ .orderByDesc(OperatorLogDO::getId);
+ }
+
+}
diff --git a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/config/AppAuthenticationConfig.java b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/config/AppAuthenticationConfig.java
new file mode 100644
index 00000000..1abc66d4
--- /dev/null
+++ b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/config/AppAuthenticationConfig.java
@@ -0,0 +1,44 @@
+package com.orion.ops.module.infra.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * 应用认证配置
+ *
+ * @author Jiahang Li
+ * @version 1.0.0
+ * @since 2024/3/5 18:26
+ */
+@Data
+@Component
+@ConfigurationProperties("app.authentication")
+public class AppAuthenticationConfig {
+
+ /**
+ * 是否允许多端登录
+ */
+ private Boolean allowMultiDevice;
+
+ /**
+ * 是否允许凭证续签
+ */
+ private Boolean allowRefresh;
+
+ /**
+ * 凭证续签最大次数
+ */
+ private Integer maxRefreshCount;
+
+ /**
+ * 登录失败锁定次数
+ */
+ private Integer loginFailedLockCount;
+
+ /**
+ * 登录失败锁定时间 (分)
+ */
+ private Integer loginFailedLockTime;
+
+}
diff --git a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/controller/OperatorLogController.http b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/controller/OperatorLogController.http
index 7a81850a..7fc7ed88 100644
--- a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/controller/OperatorLogController.http
+++ b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/controller/OperatorLogController.http
@@ -14,9 +14,4 @@ Authorization: {{token}}
"endTime": ""
}
-### 查询登录日志
-GET {{baseUrl}}/infra/operator-log/login-history?username=admin
-Content-Type: application/json
-Authorization: {{token}}
-
###
\ No newline at end of file
diff --git a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/controller/OperatorLogController.java b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/controller/OperatorLogController.java
index 4e806383..ee76bc90 100644
--- a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/controller/OperatorLogController.java
+++ b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/controller/OperatorLogController.java
@@ -1,16 +1,17 @@
package com.orion.ops.module.infra.controller;
import com.orion.lang.define.wrapper.DataGrid;
+import com.orion.ops.framework.biz.operator.log.core.annotation.OperatorLog;
import com.orion.ops.framework.common.validator.group.Page;
import com.orion.ops.framework.log.core.annotation.IgnoreLog;
import com.orion.ops.framework.log.core.enums.IgnoreLogMode;
-import com.orion.ops.framework.security.core.utils.SecurityUtils;
import com.orion.ops.framework.web.core.annotation.RestWrapper;
+import com.orion.ops.module.infra.define.operator.OperatorLogOperatorType;
import com.orion.ops.module.infra.entity.request.operator.OperatorLogQueryRequest;
-import com.orion.ops.module.infra.entity.vo.LoginHistoryVO;
import com.orion.ops.module.infra.entity.vo.OperatorLogVO;
import com.orion.ops.module.infra.service.OperatorLogService;
import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.prepost.PreAuthorize;
@@ -47,12 +48,27 @@ public class OperatorLogController {
return operatorLogService.getOperatorLogPage(request);
}
- @IgnoreLog(IgnoreLogMode.RET)
- @GetMapping("/login-history")
- @Operation(summary = "查询用户登录日志")
- @PreAuthorize("@ss.hasPermission('infra:system-user:login-history')")
- public List getLoginHistory(@RequestParam("username") String username) {
- return operatorLogService.getLoginHistory(username);
+ @OperatorLog(OperatorLogOperatorType.DELETE)
+ @DeleteMapping("/delete")
+ @Operation(summary = "删除操作日志")
+ @Parameter(name = "idList", description = "idList", required = true)
+ @PreAuthorize("@ss.hasPermission('infra:operator-log:delete')")
+ public Integer deleteOperatorLog(@RequestParam("idList") List idList) {
+ return operatorLogService.deleteOperatorLog(idList);
+ }
+
+ @PostMapping("/query-count")
+ @Operation(summary = "查询操作日志数量")
+ public Long getOperatorLogCount(@RequestBody OperatorLogQueryRequest request) {
+ return operatorLogService.getOperatorLogCount(request);
+ }
+
+ @OperatorLog(OperatorLogOperatorType.CLEAR)
+ @PostMapping("/clear")
+ @Operation(summary = "清空操作日志")
+ @PreAuthorize("@ss.hasPermission('infra:operator-log:clear')")
+ public Integer clearOperatorLog(@RequestBody OperatorLogQueryRequest request) {
+ return operatorLogService.clearOperatorLog(request);
}
}
diff --git a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/controller/SystemUserController.http b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/controller/SystemUserController.http
index 05a8ee68..b3906f61 100644
--- a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/controller/SystemUserController.http
+++ b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/controller/SystemUserController.http
@@ -67,4 +67,10 @@ DELETE {{baseUrl}}/infra/system-user/delete?id=1
Authorization: {{token}}
+### 查询登录日志
+GET {{baseUrl}}/infra/system-user/login-history?username=admin
+Content-Type: application/json
+Authorization: {{token}}
+
+
###
diff --git a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/controller/SystemUserController.java b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/controller/SystemUserController.java
index edec1acf..7f89be81 100644
--- a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/controller/SystemUserController.java
+++ b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/controller/SystemUserController.java
@@ -10,8 +10,10 @@ import com.orion.ops.framework.log.core.enums.IgnoreLogMode;
import com.orion.ops.framework.web.core.annotation.RestWrapper;
import com.orion.ops.module.infra.define.operator.SystemUserOperatorType;
import com.orion.ops.module.infra.entity.request.user.*;
+import com.orion.ops.module.infra.entity.vo.LoginHistoryVO;
import com.orion.ops.module.infra.entity.vo.SystemUserVO;
import com.orion.ops.module.infra.entity.vo.UserSessionVO;
+import com.orion.ops.module.infra.service.OperatorLogService;
import com.orion.ops.module.infra.service.SystemUserManagementService;
import com.orion.ops.module.infra.service.SystemUserRoleService;
import com.orion.ops.module.infra.service.SystemUserService;
@@ -51,6 +53,9 @@ public class SystemUserController {
@Resource
private SystemUserManagementService systemUserManagementService;
+ @Resource
+ private OperatorLogService operatorLogService;
+
@OperatorLog(SystemUserOperatorType.CREATE)
@PostMapping("/create")
@Operation(summary = "创建用户")
@@ -159,5 +164,12 @@ public class SystemUserController {
return HttpWrapper.ok();
}
-}
+ @IgnoreLog(IgnoreLogMode.RET)
+ @GetMapping("/login-history")
+ @Operation(summary = "查询用户登录日志")
+ @PreAuthorize("@ss.hasPermission('infra:system-user:login-history')")
+ public List getLoginHistory(@RequestParam("username") String username) {
+ return operatorLogService.getLoginHistory(username);
+ }
+}
diff --git a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/convert/OperatorLogProviderConvert.java b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/convert/OperatorLogProviderConvert.java
new file mode 100644
index 00000000..f83fa5c2
--- /dev/null
+++ b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/convert/OperatorLogProviderConvert.java
@@ -0,0 +1,22 @@
+package com.orion.ops.module.infra.convert;
+
+import com.orion.ops.module.infra.entity.domain.OperatorLogDO;
+import com.orion.ops.module.infra.entity.dto.operator.OperatorLogDTO;
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+
+/**
+ * 操作日志 对外对象转换器
+ *
+ * @author Jiahang Li
+ * @version 1.0.0
+ * @since 2023-10-10 17:08
+ */
+@Mapper
+public interface OperatorLogProviderConvert {
+
+ OperatorLogProviderConvert MAPPER = Mappers.getMapper(OperatorLogProviderConvert.class);
+
+ OperatorLogDTO to(OperatorLogDO domain);
+
+}
diff --git a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/define/cache/UserCacheKeyDefine.java b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/define/cache/UserCacheKeyDefine.java
index d02ac7da..e095b658 100644
--- a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/define/cache/UserCacheKeyDefine.java
+++ b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/define/cache/UserCacheKeyDefine.java
@@ -38,7 +38,6 @@ public interface UserCacheKeyDefine {
.desc("用户登录失败次数 ${username}")
.type(Integer.class)
.struct(RedisCacheStruct.STRING)
- .timeout(3, TimeUnit.DAYS)
.build();
CacheKeyDefine LOGIN_TOKEN = new CacheKeyBuilder()
diff --git a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/define/operator/OperatorLogOperatorType.java b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/define/operator/OperatorLogOperatorType.java
new file mode 100644
index 00000000..4e781b92
--- /dev/null
+++ b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/define/operator/OperatorLogOperatorType.java
@@ -0,0 +1,31 @@
+package com.orion.ops.module.infra.define.operator;
+
+import com.orion.ops.framework.biz.operator.log.core.annotation.Module;
+import com.orion.ops.framework.biz.operator.log.core.factory.InitializingOperatorTypes;
+import com.orion.ops.framework.biz.operator.log.core.model.OperatorType;
+
+import static com.orion.ops.framework.biz.operator.log.core.enums.OperatorRiskLevel.H;
+
+/**
+ * 操作日志 操作日志类型
+ *
+ * @author Jiahang Li
+ * @version 1.0.0
+ * @since 2024-3-4 16:20
+ */
+@Module("infra:operator-log")
+public class OperatorLogOperatorType extends InitializingOperatorTypes {
+
+ public static final String DELETE = "operator-log:delete";
+
+ public static final String CLEAR = "operator-log:clear";
+
+ @Override
+ public OperatorType[] types() {
+ return new OperatorType[]{
+ new OperatorType(H, DELETE, "删除操作日志 ${count} 条"),
+ new OperatorType(H, CLEAR, "清空操作日志 ${count} 条"),
+ };
+ }
+
+}
diff --git a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/service/AuthenticationService.java b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/service/AuthenticationService.java
index b0589e0b..d23413e1 100644
--- a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/service/AuthenticationService.java
+++ b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/service/AuthenticationService.java
@@ -16,16 +16,6 @@ import javax.servlet.http.HttpServletRequest;
*/
public interface AuthenticationService {
- // TODO 配置化
- // 允许多端登录
- boolean allowMultiDevice = true;
- // 允许凭证续签
- boolean allowRefresh = true;
- // 凭证续签最大次数
- int maxRefreshCount = 3;
- // 失败锁定次数
- int maxFailedLoginCount = 5;
-
/**
* 登录
*
diff --git a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/service/OperatorLogService.java b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/service/OperatorLogService.java
index 35322bed..7a4e9f43 100644
--- a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/service/OperatorLogService.java
+++ b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/service/OperatorLogService.java
@@ -32,6 +32,30 @@ public interface OperatorLogService {
*/
DataGrid getOperatorLogPage(OperatorLogQueryRequest request);
+ /**
+ * 删除操作日志
+ *
+ * @param idList idList
+ * @return effect
+ */
+ Integer deleteOperatorLog(List idList);
+
+ /**
+ * 查询操作日志数量
+ *
+ * @param request request
+ * @return count
+ */
+ Long getOperatorLogCount(OperatorLogQueryRequest request);
+
+ /**
+ * 清空操作日志
+ *
+ * @param request request
+ * @return effect
+ */
+ Integer clearOperatorLog(OperatorLogQueryRequest request);
+
/**
* 查询用户登录日志
*
diff --git a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/service/impl/AuthenticationServiceImpl.java b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/service/impl/AuthenticationServiceImpl.java
index c7fadf28..ac8a2530 100644
--- a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/service/impl/AuthenticationServiceImpl.java
+++ b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/service/impl/AuthenticationServiceImpl.java
@@ -16,6 +16,7 @@ import com.orion.ops.framework.common.utils.Valid;
import com.orion.ops.framework.redis.core.utils.RedisStrings;
import com.orion.ops.framework.redis.core.utils.RedisUtils;
import com.orion.ops.framework.security.core.utils.SecurityUtils;
+import com.orion.ops.module.infra.config.AppAuthenticationConfig;
import com.orion.ops.module.infra.convert.SystemUserConvert;
import com.orion.ops.module.infra.dao.SystemUserDAO;
import com.orion.ops.module.infra.dao.SystemUserRoleDAO;
@@ -39,6 +40,7 @@ import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
@@ -51,6 +53,9 @@ import java.util.stream.Collectors;
@Service
public class AuthenticationServiceImpl implements AuthenticationService {
+ @Resource
+ private AppAuthenticationConfig appAuthenticationConfig;
+
@Resource
private SystemUserDAO systemUserDAO;
@@ -95,7 +100,7 @@ public class AuthenticationServiceImpl implements AuthenticationService {
String userAgent = Servlets.getUserAgent(servletRequest);
long current = System.currentTimeMillis();
// 不允许多端登录
- if (!allowMultiDevice) {
+ if (!appAuthenticationConfig.getAllowMultiDevice()) {
// 无效化其他缓存
this.invalidOtherDeviceToken(user.getId(), current, remoteAddr, location, userAgent);
}
@@ -157,7 +162,7 @@ public class AuthenticationServiceImpl implements AuthenticationService {
return JSON.parseObject(loginCache, LoginTokenDTO.class);
}
// loginToken 不存在 需要查询 refreshToken
- if (!allowRefresh) {
+ if (!appAuthenticationConfig.getAllowRefresh()) {
return null;
}
String refreshKey = UserCacheKeyDefine.LOGIN_REFRESH.format(pair.getKey(), pair.getValue());
@@ -172,7 +177,7 @@ public class AuthenticationServiceImpl implements AuthenticationService {
refresh.setRefreshCount(refreshCount);
// 设置登录缓存
RedisStrings.setJson(loginKey, UserCacheKeyDefine.LOGIN_TOKEN, refresh);
- if (refreshCount < maxRefreshCount) {
+ if (refreshCount < appAuthenticationConfig.getMaxRefreshCount()) {
// 小于续签最大次数 则再次设置 refreshToken
RedisStrings.setJson(refreshKey, UserCacheKeyDefine.LOGIN_REFRESH, refresh);
} else {
@@ -214,7 +219,8 @@ public class AuthenticationServiceImpl implements AuthenticationService {
// 检查登录失败次数
String failedCountKey = UserCacheKeyDefine.LOGIN_FAILED_COUNT.format(request.getUsername());
String failedCount = redisTemplate.opsForValue().get(failedCountKey);
- if (failedCount != null && Integer.parseInt(failedCount) >= maxFailedLoginCount) {
+ if (failedCount != null
+ && Integer.parseInt(failedCount) >= appAuthenticationConfig.getLoginFailedLockCount()) {
throw Exceptions.argument(ErrorMessage.MAX_LOGIN_FAILED);
}
}
@@ -235,23 +241,23 @@ public class AuthenticationServiceImpl implements AuthenticationService {
// 刷新登录失败缓存
String failedCountKey = UserCacheKeyDefine.LOGIN_FAILED_COUNT.format(request.getUsername());
Long failedLoginCount = redisTemplate.opsForValue().increment(failedCountKey);
- RedisUtils.setExpire(failedCountKey, UserCacheKeyDefine.LOGIN_FAILED_COUNT);
- // 锁定用户
- if (failedLoginCount >= maxFailedLoginCount) {
- // 更新用户表
- SystemUserDO updateUser = new SystemUserDO();
- updateUser.setId(user.getId());
- updateUser.setStatus(UserStatusEnum.LOCKED.getStatus());
- systemUserDAO.updateById(updateUser);
- // 修改缓存状态
- String userInfoKey = UserCacheKeyDefine.USER_INFO.format(user.getId());
- String userInfoCache = redisTemplate.opsForValue().get(userInfoKey);
- if (userInfoCache != null) {
- LoginUser loginUser = JSON.parseObject(userInfoCache, LoginUser.class);
- loginUser.setStatus(UserStatusEnum.LOCKED.getStatus());
- RedisStrings.setJson(userInfoKey, UserCacheKeyDefine.USER_INFO, loginUser);
- }
- }
+ RedisUtils.setExpire(failedCountKey, appAuthenticationConfig.getLoginFailedLockTime(), TimeUnit.MINUTES);
+ // // 锁定用户
+ // if (failedLoginCount >= appAuthenticationConfig.getLoginFailedLockCount()) {
+ // // 更新用户表
+ // SystemUserDO updateUser = new SystemUserDO();
+ // updateUser.setId(user.getId());
+ // updateUser.setStatus(UserStatusEnum.LOCKED.getStatus());
+ // systemUserDAO.updateById(updateUser);
+ // // 修改缓存状态
+ // String userInfoKey = UserCacheKeyDefine.USER_INFO.format(user.getId());
+ // String userInfoCache = redisTemplate.opsForValue().get(userInfoKey);
+ // if (userInfoCache != null) {
+ // LoginUser loginUser = JSON.parseObject(userInfoCache, LoginUser.class);
+ // loginUser.setStatus(UserStatusEnum.LOCKED.getStatus());
+ // RedisStrings.setJson(userInfoKey, UserCacheKeyDefine.USER_INFO, loginUser);
+ // }
+ // }
return false;
}
@@ -337,7 +343,7 @@ public class AuthenticationServiceImpl implements AuthenticationService {
}
}
// 删除续签信息
- if (allowRefresh) {
+ if (appAuthenticationConfig.getAllowRefresh()) {
RedisUtils.scanKeysDelete(UserCacheKeyDefine.LOGIN_REFRESH.format(id, "*"));
}
}
@@ -365,7 +371,7 @@ public class AuthenticationServiceImpl implements AuthenticationService {
.build();
RedisStrings.setJson(loginKey, UserCacheKeyDefine.LOGIN_TOKEN, loginValue);
// 生成 refreshToken
- if (allowRefresh) {
+ if (appAuthenticationConfig.getAllowRefresh()) {
String refreshKey = UserCacheKeyDefine.LOGIN_REFRESH.format(id, loginTime);
RedisStrings.setJson(refreshKey, UserCacheKeyDefine.LOGIN_REFRESH, loginValue);
}
diff --git a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/service/impl/OperatorLogServiceImpl.java b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/service/impl/OperatorLogServiceImpl.java
index 4ae801e5..c7538dba 100644
--- a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/service/impl/OperatorLogServiceImpl.java
+++ b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/service/impl/OperatorLogServiceImpl.java
@@ -1,9 +1,11 @@
package com.orion.ops.module.infra.service.impl;
+import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.orion.lang.define.wrapper.DataGrid;
import com.orion.lang.utils.Arrays1;
import com.orion.ops.framework.biz.operator.log.core.model.OperatorLogModel;
+import com.orion.ops.framework.biz.operator.log.core.utils.OperatorLogs;
import com.orion.ops.framework.common.constant.Const;
import com.orion.ops.module.infra.convert.OperatorLogConvert;
import com.orion.ops.module.infra.dao.OperatorLogDAO;
@@ -51,6 +53,33 @@ public class OperatorLogServiceImpl implements OperatorLogService {
.dataGrid(OperatorLogConvert.MAPPER::to);
}
+ @Override
+ public Integer deleteOperatorLog(List idList) {
+ log.info("OperatorLogService.deleteOperatorLog start {}", JSON.toJSONString(idList));
+ int effect = operatorLogDAO.deleteBatchIds(idList);
+ log.info("OperatorLogService.deleteOperatorLog finish {}", effect);
+ // 设置日志参数
+ OperatorLogs.add(OperatorLogs.COUNT, effect);
+ return effect;
+ }
+
+ @Override
+ public Long getOperatorLogCount(OperatorLogQueryRequest request) {
+ return operatorLogDAO.selectCount(this.buildQueryWrapper(request));
+ }
+
+ @Override
+ public Integer clearOperatorLog(OperatorLogQueryRequest request) {
+ log.info("OperatorLogService.clearOperatorLog start {}", JSON.toJSONString(request));
+ // 删除
+ LambdaQueryWrapper wrapper = this.buildQueryWrapper(request);
+ int effect = operatorLogDAO.delete(wrapper);
+ log.info("OperatorLogService.clearOperatorLog finish {}", effect);
+ // 设置日志参数
+ OperatorLogs.add(OperatorLogs.COUNT, effect);
+ return effect;
+ }
+
@Override
public List getLoginHistory(String username) {
// 条件
diff --git a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/service/impl/SystemUserServiceImpl.java b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/service/impl/SystemUserServiceImpl.java
index 0c1b698c..9c79ec89 100644
--- a/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/service/impl/SystemUserServiceImpl.java
+++ b/orion-ops-module-infra/orion-ops-module-infra-service/src/main/java/com/orion/ops/module/infra/service/impl/SystemUserServiceImpl.java
@@ -14,6 +14,7 @@ import com.orion.ops.framework.redis.core.utils.RedisStrings;
import com.orion.ops.framework.redis.core.utils.RedisUtils;
import com.orion.ops.framework.redis.core.utils.barrier.CacheBarriers;
import com.orion.ops.framework.security.core.utils.SecurityUtils;
+import com.orion.ops.module.infra.config.AppAuthenticationConfig;
import com.orion.ops.module.infra.convert.SystemUserConvert;
import com.orion.ops.module.infra.dao.OperatorLogDAO;
import com.orion.ops.module.infra.dao.SystemRoleDAO;
@@ -49,6 +50,9 @@ import java.util.stream.Collectors;
@Service
public class SystemUserServiceImpl implements SystemUserService {
+ @Resource
+ private AppAuthenticationConfig appAuthenticationConfig;
+
@Resource
private SystemUserDAO systemUserDAO;
@@ -274,7 +278,7 @@ public class SystemUserServiceImpl implements SystemUserService {
// 删除登录缓存
RedisUtils.scanKeysDelete(UserCacheKeyDefine.LOGIN_TOKEN.format(id, "*"));
// 删除续签信息
- if (AuthenticationService.allowRefresh) {
+ if (appAuthenticationConfig.getAllowRefresh()) {
RedisUtils.scanKeysDelete(UserCacheKeyDefine.LOGIN_REFRESH.format(id, "*"));
}
}
diff --git a/orion-ops-ui/.env.development b/orion-ops-ui/.env.development
index 3d2dd973..d242a228 100644
--- a/orion-ops-ui/.env.development
+++ b/orion-ops-ui/.env.development
@@ -1,4 +1,4 @@
VITE_API_BASE_URL= 'http://127.0.0.1:9200/orion/api'
VITE_WS_BASE_URL= 'ws://127.0.0.1:9200/orion/keep-alive'
-VITE_APP_VERSION= '1.0.0'
+VITE_APP_VERSION= '1.0.1'
VITE_SFTP_PREVIEW_MB= 2
diff --git a/orion-ops-ui/.env.production b/orion-ops-ui/.env.production
index 90593613..5ca34720 100644
--- a/orion-ops-ui/.env.production
+++ b/orion-ops-ui/.env.production
@@ -1,4 +1,4 @@
VITE_API_BASE_URL= '/orion/api'
VITE_WS_BASE_URL= '/orion/keep-alive'
-VITE_APP_VERSION= '1.0.0'
+VITE_APP_VERSION= '1.0.1'
VITE_SFTP_PREVIEW_MB= 2
diff --git a/orion-ops-ui/package.json b/orion-ops-ui/package.json
index 12a9d4d8..bdf238ca 100644
--- a/orion-ops-ui/package.json
+++ b/orion-ops-ui/package.json
@@ -1,7 +1,7 @@
{
"name": "orion-ops-pro-ui",
"description": "Orion Ops Pro for Vue",
- "version": "1.0.0",
+ "version": "1.0.1",
"private": true,
"author": "Jiahang Li",
"license": "Apache 2.0",
diff --git a/orion-ops-ui/src/api/asset/host-connect-log.ts b/orion-ops-ui/src/api/asset/host-connect-log.ts
index ee983a68..c539b4b6 100644
--- a/orion-ops-ui/src/api/asset/host-connect-log.ts
+++ b/orion-ops-ui/src/api/asset/host-connect-log.ts
@@ -1,11 +1,13 @@
import type { DataGrid, Pagination } from '@/types/global';
import type { TableData } from '@arco-design/web-vue/es/table/interface';
import axios from 'axios';
+import qs from 'query-string';
/**
* 主机连接日志查询请求
*/
export interface HostConnectLogQueryRequest extends Pagination {
+ id?: number;
userId?: number;
hostId?: number;
hostAddress?: string;
@@ -21,6 +23,7 @@ export interface HostConnectLogQueryRequest extends Pagination {
export interface HostConnectLogQueryResponse extends TableData {
id: number;
userId: number;
+ username: number;
hostId: number;
hostName: string;
hostAddress: string;
@@ -29,11 +32,20 @@ export interface HostConnectLogQueryResponse extends TableData {
status: string;
startTime: number;
endTime: number;
- extraInfo: string;
- createTime: number;
- updateTime: number;
- creator: string;
- updater: string;
+ extra: HostConnectLogExtra;
+}
+
+/**
+ * 主机连接日志拓展对象
+ */
+export interface HostConnectLogExtra {
+ traceId: string;
+ channelId: string;
+ sessionId: string;
+ address: string;
+ location: string;
+ userAgent: string;
+ errorMessage: string;
}
/**
@@ -52,3 +64,36 @@ export function getLatestConnectHostId(type: string, limit: number) {
limit
});
}
+
+/**
+ * 删除主机连接日志
+ */
+export function deleteHostConnectLog(idList: Array) {
+ return axios.delete('/asset/host-connect-log/delete', {
+ params: { idList },
+ paramsSerializer: params => {
+ return qs.stringify(params, { arrayFormat: 'comma' });
+ }
+ });
+}
+
+/**
+ * 查询主机连接日志数量
+ */
+export function getHostConnectLogCount(request: HostConnectLogQueryRequest) {
+ return axios.post('/asset/host-connect-log/query-count', request);
+}
+
+/**
+ * 清空主机连接日志
+ */
+export function clearHostConnectLog(request: HostConnectLogQueryRequest) {
+ return axios.post('/asset/host-connect-log/clear', request);
+}
+
+/**
+ * 强制断开主机连接
+ */
+export function hostForceOffline(request: HostConnectLogQueryRequest) {
+ return axios.put('/asset/host-connect-log/force-offline', request);
+}
diff --git a/orion-ops-ui/src/api/asset/host-sftp-log.ts b/orion-ops-ui/src/api/asset/host-sftp-log.ts
new file mode 100644
index 00000000..cf2a1c7d
--- /dev/null
+++ b/orion-ops-ui/src/api/asset/host-sftp-log.ts
@@ -0,0 +1,62 @@
+import type { DataGrid, Pagination } from '@/types/global';
+import type { TableData } from '@arco-design/web-vue/es/table/interface';
+import axios from 'axios';
+import qs from 'query-string';
+
+/**
+ * SFTP 操作日志 查询请求
+ */
+export interface HostSftpLogQueryRequest extends Pagination {
+ userId?: number;
+ hostId?: number;
+ type?: string;
+ result?: number;
+ startTimeRange?: string[];
+}
+
+/**
+ * SFTP 操作日志 查询响应
+ */
+export interface HostSftpLogQueryResponse extends TableData {
+ id: number;
+ userId: number;
+ username: number;
+ hostId: number;
+ hostName: string;
+ hostAddress: string;
+ address: string;
+ location: string;
+ userAgent: string;
+ paths: string[];
+ type: string;
+ result: string;
+ startTime: number;
+ extra: HostSftpLogExtra;
+}
+
+/**
+ * SFTP 操作日志 拓展对象
+ */
+export interface HostSftpLogExtra {
+ mod: number;
+ target: string;
+}
+
+/**
+ * 分页查询 SFTP 操作日志
+ */
+export function getHostSftpLogPage(request: HostSftpLogQueryRequest) {
+ return axios.post>('/asset/host-sftp-log/query', request);
+}
+
+/**
+ * 删除 SFTP 操作日志
+ */
+export function deleteHostSftpLog(idList: Array) {
+ return axios.delete('/asset/host-sftp-log/delete', {
+ params: { idList },
+ paramsSerializer: params => {
+ return qs.stringify(params, { arrayFormat: 'comma' });
+ }
+ });
+}
diff --git a/orion-ops-ui/src/api/user/mine.ts b/orion-ops-ui/src/api/user/mine.ts
index 997a848b..0192299e 100644
--- a/orion-ops-ui/src/api/user/mine.ts
+++ b/orion-ops-ui/src/api/user/mine.ts
@@ -1,6 +1,6 @@
import type { DataGrid } from '@/types/global';
-import type { LoginHistoryQueryResponse, OperatorLogQueryRequest, OperatorLogQueryResponse } from './operator-log';
-import type { UserQueryResponse, UserSessionOfflineRequest, UserSessionQueryResponse, UserUpdateRequest } from './user';
+import type { OperatorLogQueryRequest, OperatorLogQueryResponse } from './operator-log';
+import type { LoginHistoryQueryResponse, UserQueryResponse, UserSessionOfflineRequest, UserSessionQueryResponse, UserUpdateRequest } from './user';
import axios from 'axios';
/**
diff --git a/orion-ops-ui/src/api/user/operator-log.ts b/orion-ops-ui/src/api/user/operator-log.ts
index 73e553ac..a1e652d7 100644
--- a/orion-ops-ui/src/api/user/operator-log.ts
+++ b/orion-ops-ui/src/api/user/operator-log.ts
@@ -1,5 +1,6 @@
import type { DataGrid, Pagination } from '@/types/global';
import axios from 'axios';
+import qs from 'query-string';
/**
* 操作日志查询参数
@@ -40,19 +41,6 @@ export interface OperatorLogQueryResponse {
createTime: number;
}
-/**
- * 登录日志查询响应
- */
-export interface LoginHistoryQueryResponse {
- id: number;
- address: string;
- location: string;
- userAgent: string;
- result: number;
- errorMessage: string;
- createTime: number;
-}
-
/**
* 分页操作日志
*/
@@ -61,8 +49,27 @@ export function getOperatorLogPage(request: OperatorLogQueryRequest) {
}
/**
- * 查询登录日志
+ * 删除操作日志
*/
-export function getLoginHistory(username: string) {
- return axios.get('/infra/operator-log/login-history', { params: { username } });
+export function deleteOperatorLog(idList: Array) {
+ return axios.delete('/infra/operator-log/delete', {
+ params: { idList },
+ paramsSerializer: params => {
+ return qs.stringify(params, { arrayFormat: 'comma' });
+ }
+ });
+}
+
+/**
+ * 查询操作日志数量
+ */
+export function getOperatorLogCount(request: OperatorLogQueryRequest) {
+ return axios.post('/infra/operator-log/query-count', request);
+}
+
+/**
+ * 清空操作日志
+ */
+export function clearOperatorLog(request: OperatorLogQueryRequest) {
+ return axios.post('/infra/operator-log/clear', request);
}
diff --git a/orion-ops-ui/src/api/user/user.ts b/orion-ops-ui/src/api/user/user.ts
index 4b095f82..63c30738 100644
--- a/orion-ops-ui/src/api/user/user.ts
+++ b/orion-ops-ui/src/api/user/user.ts
@@ -77,6 +77,19 @@ export interface UserSessionOfflineRequest {
timestamp: number;
}
+/**
+ * 登录日志查询响应
+ */
+export interface LoginHistoryQueryResponse {
+ id: number;
+ address: string;
+ location: string;
+ userAgent: string;
+ result: number;
+ errorMessage: string;
+ createTime: number;
+}
+
/**
* 创建用户
*/
@@ -160,3 +173,10 @@ export function getUserSessionList(id: number) {
export function offlineUserSession(request: UserSessionOfflineRequest) {
return axios.put('/infra/system-user/session/offline', request);
}
+
+/**
+ * 查询登录日志
+ */
+export function getLoginHistory(username: string) {
+ return axios.get('/infra/system-user/login-history', { params: { username } });
+}
diff --git a/orion-ops-ui/src/assets/style/global.less b/orion-ops-ui/src/assets/style/global.less
index 890ede99..186e25e3 100644
--- a/orion-ops-ui/src/assets/style/global.less
+++ b/orion-ops-ui/src/assets/style/global.less
@@ -230,6 +230,20 @@ body {
margin-bottom: 16px;
}
+.text-ellipsis {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.text-copy {
+ &:hover {
+ cursor: pointer;
+ text-decoration: underline;
+ color: rgb(var(--arcoblue-6))
+ }
+}
+
.copy-left, .copy-right {
color: rgb(var(--arcoblue-6));
cursor: pointer;
diff --git a/orion-ops-ui/src/components/app/footer/index.vue b/orion-ops-ui/src/components/app/footer/index.vue
index d087755a..3f0e9627 100644
--- a/orion-ops-ui/src/components/app/footer/index.vue
+++ b/orion-ops-ui/src/components/app/footer/index.vue
@@ -1,6 +1,6 @@