✨ SFTP 操作日志.
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
### 分页查询 SFTP 操作日志
|
### 分页查询 SFTP 操作日志
|
||||||
POST {{baseUrl}}/asset/sftp-log/query
|
POST {{baseUrl}}/asset/host-sftp-log/query
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
Authorization: {{token}}
|
Authorization: {{token}}
|
||||||
|
|
||||||
@@ -9,4 +9,9 @@ Authorization: {{token}}
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
### 删除 SFTP 操作日志
|
||||||
|
DELETE {{baseUrl}}/asset/host-sftp-log/delete?idList=1,2,3
|
||||||
|
Authorization: {{token}}
|
||||||
|
|
||||||
|
|
||||||
###
|
###
|
||||||
|
|||||||
@@ -1,24 +1,25 @@
|
|||||||
package com.orion.ops.module.asset.controller;
|
package com.orion.ops.module.asset.controller;
|
||||||
|
|
||||||
import com.orion.lang.define.wrapper.DataGrid;
|
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.common.validator.group.Page;
|
||||||
import com.orion.ops.framework.log.core.annotation.IgnoreLog;
|
import com.orion.ops.framework.log.core.annotation.IgnoreLog;
|
||||||
import com.orion.ops.framework.log.core.enums.IgnoreLogMode;
|
import com.orion.ops.framework.log.core.enums.IgnoreLogMode;
|
||||||
import com.orion.ops.framework.web.core.annotation.RestWrapper;
|
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.request.host.HostSftpLogQueryRequest;
|
||||||
import com.orion.ops.module.asset.entity.vo.HostSftpLogVO;
|
import com.orion.ops.module.asset.entity.vo.HostSftpLogVO;
|
||||||
import com.orion.ops.module.asset.service.HostSftpLogService;
|
import com.orion.ops.module.asset.service.HostSftpLogService;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SFTP 操作日志服务 api
|
* SFTP 操作日志服务 api
|
||||||
@@ -32,7 +33,7 @@ import javax.annotation.Resource;
|
|||||||
@Validated
|
@Validated
|
||||||
@RestWrapper
|
@RestWrapper
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/asset/sftp-log")
|
@RequestMapping("/asset/host-sftp-log")
|
||||||
@SuppressWarnings({"ELValidationInJSP", "SpringElInspection"})
|
@SuppressWarnings({"ELValidationInJSP", "SpringElInspection"})
|
||||||
public class HostSftpLogController {
|
public class HostSftpLogController {
|
||||||
|
|
||||||
@@ -42,9 +43,18 @@ public class HostSftpLogController {
|
|||||||
@IgnoreLog(IgnoreLogMode.RET)
|
@IgnoreLog(IgnoreLogMode.RET)
|
||||||
@PostMapping("/query")
|
@PostMapping("/query")
|
||||||
@Operation(summary = "分页查询 SFTP 操作日志")
|
@Operation(summary = "分页查询 SFTP 操作日志")
|
||||||
@PreAuthorize("@ss.hasPermission('infra:operator-log:query')")
|
@PreAuthorize("@ss.hasAnyPermission('infra:operator-log:query', 'asset:host-sftp-log:management:query')")
|
||||||
public DataGrid<HostSftpLogVO> querySftpLogPage(@Validated(Page.class) @RequestBody HostSftpLogQueryRequest request) {
|
public DataGrid<HostSftpLogVO> getHostSftpLogPage(@Validated(Page.class) @RequestBody HostSftpLogQueryRequest request) {
|
||||||
return hostSftpLogService.querySftpLogPage(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<Long> idList) {
|
||||||
|
return hostSftpLogService.deleteHostSftpLog(idList);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ public class HostTerminalOperatorType extends InitializingOperatorTypes {
|
|||||||
|
|
||||||
public static final String CONNECT = "host-terminal:connect";
|
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_MKDIR = "host-terminal:sftp-mkdir";
|
||||||
|
|
||||||
public static final String SFTP_TOUCH = "host-terminal:sftp-touch";
|
public static final String SFTP_TOUCH = "host-terminal:sftp-touch";
|
||||||
@@ -55,6 +57,7 @@ public class HostTerminalOperatorType extends InitializingOperatorTypes {
|
|||||||
public OperatorType[] types() {
|
public OperatorType[] types() {
|
||||||
return new OperatorType[]{
|
return new OperatorType[]{
|
||||||
new OperatorType(L, CONNECT, "连接主机 ${connectType} <sb>${hostName}</sb>"),
|
new OperatorType(L, CONNECT, "连接主机 ${connectType} <sb>${hostName}</sb>"),
|
||||||
|
new OperatorType(H, DELETE_SFTP_LOG, "删除 SFTP 操作日志 <sb>${count}</sb> 条"),
|
||||||
new OperatorType(L, SFTP_MKDIR, "创建文件夹 ${hostName} <sb>${path}</sb>"),
|
new OperatorType(L, SFTP_MKDIR, "创建文件夹 ${hostName} <sb>${path}</sb>"),
|
||||||
new OperatorType(L, SFTP_TOUCH, "创建文件 ${hostName} <sb>${path}</sb>"),
|
new OperatorType(L, SFTP_TOUCH, "创建文件 ${hostName} <sb>${path}</sb>"),
|
||||||
new OperatorType(M, SFTP_MOVE, "移动文件 ${hostName} <sb>${path}</sb> 至 <sb>${target}</sb>"),
|
new OperatorType(M, SFTP_MOVE, "移动文件 ${hostName} <sb>${path}</sb> 至 <sb>${target}</sb>"),
|
||||||
|
|||||||
@@ -32,14 +32,20 @@ public class HostSftpLogVO implements Serializable {
|
|||||||
@Schema(description = "用户id")
|
@Schema(description = "用户id")
|
||||||
private Long userId;
|
private Long userId;
|
||||||
|
|
||||||
@Schema(description = "主机id")
|
|
||||||
private Long hostId;
|
|
||||||
|
|
||||||
@Schema(description = "用户名")
|
@Schema(description = "用户名")
|
||||||
private String username;
|
private String username;
|
||||||
|
|
||||||
@Schema(description = "traceId")
|
@Schema(description = "主机id")
|
||||||
private String traceId;
|
private Long hostId;
|
||||||
|
|
||||||
|
@Schema(description = "主机名称")
|
||||||
|
private String hostName;
|
||||||
|
|
||||||
|
@Schema(description = "主机地址")
|
||||||
|
private String hostAddress;
|
||||||
|
|
||||||
|
@Schema(description = "操作文件")
|
||||||
|
private String[] paths;
|
||||||
|
|
||||||
@Schema(description = "请求ip")
|
@Schema(description = "请求ip")
|
||||||
private String address;
|
private String address;
|
||||||
@@ -50,40 +56,16 @@ public class HostSftpLogVO implements Serializable {
|
|||||||
@Schema(description = "userAgent")
|
@Schema(description = "userAgent")
|
||||||
private String userAgent;
|
private String userAgent;
|
||||||
|
|
||||||
@Schema(description = "风险等级")
|
|
||||||
private String riskLevel;
|
|
||||||
|
|
||||||
@Schema(description = "模块")
|
|
||||||
private String module;
|
|
||||||
|
|
||||||
@Schema(description = "操作类型")
|
@Schema(description = "操作类型")
|
||||||
private String type;
|
private String type;
|
||||||
|
|
||||||
@Schema(description = "日志")
|
|
||||||
private String logInfo;
|
|
||||||
|
|
||||||
@Schema(description = "参数")
|
@Schema(description = "参数")
|
||||||
private Map<String, Object> extra;
|
private Map<String, Object> extra;
|
||||||
|
|
||||||
@Schema(description = "操作结果 0失败 1成功")
|
@Schema(description = "操作结果 0失败 1成功")
|
||||||
private Integer result;
|
private Integer result;
|
||||||
|
|
||||||
@Schema(description = "错误信息")
|
|
||||||
private String errorMessage;
|
|
||||||
|
|
||||||
@Schema(description = "返回值")
|
|
||||||
private String returnValue;
|
|
||||||
|
|
||||||
@Schema(description = "操作时间")
|
|
||||||
private Integer duration;
|
|
||||||
|
|
||||||
@Schema(description = "开始时间")
|
@Schema(description = "开始时间")
|
||||||
private Date startTime;
|
private Date startTime;
|
||||||
|
|
||||||
@Schema(description = "结束时间")
|
|
||||||
private Date endTime;
|
|
||||||
|
|
||||||
@Schema(description = "创建时间")
|
|
||||||
private Date createTime;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package com.orion.ops.module.asset.handler.host.terminal.handler;
|
|||||||
|
|
||||||
import com.orion.lang.utils.collect.Maps;
|
import com.orion.lang.utils.collect.Maps;
|
||||||
import com.orion.ops.framework.biz.operator.log.core.utils.OperatorLogs;
|
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.framework.common.enums.BooleanBit;
|
||||||
import com.orion.ops.module.asset.define.operator.HostTerminalOperatorType;
|
import com.orion.ops.module.asset.define.operator.HostTerminalOperatorType;
|
||||||
import com.orion.ops.module.asset.handler.host.terminal.enums.OutputTypeEnum;
|
import com.orion.ops.module.asset.handler.host.terminal.enums.OutputTypeEnum;
|
||||||
@@ -54,7 +53,7 @@ public class SftpRemoveHandler extends AbstractTerminalHandler<SftpBaseRequest>
|
|||||||
.build());
|
.build());
|
||||||
// 保存操作日志
|
// 保存操作日志
|
||||||
Map<String, Object> extra = Maps.newMap();
|
Map<String, Object> extra = Maps.newMap();
|
||||||
extra.put(OperatorLogs.PATH, String.join(Const.COMMA, paths));
|
extra.put(OperatorLogs.PATH, payload.getPath());
|
||||||
this.saveOperatorLog(payload, channel,
|
this.saveOperatorLog(payload, channel,
|
||||||
extra, HostTerminalOperatorType.SFTP_REMOVE,
|
extra, HostTerminalOperatorType.SFTP_REMOVE,
|
||||||
startTime, ex);
|
startTime, ex);
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import com.orion.lang.define.wrapper.DataGrid;
|
|||||||
import com.orion.ops.module.asset.entity.request.host.HostSftpLogQueryRequest;
|
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.entity.vo.HostSftpLogVO;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SFTP 操作日志 服务类
|
* SFTP 操作日志 服务类
|
||||||
*
|
*
|
||||||
@@ -19,6 +21,14 @@ public interface HostSftpLogService {
|
|||||||
* @param request request
|
* @param request request
|
||||||
* @return rows
|
* @return rows
|
||||||
*/
|
*/
|
||||||
DataGrid<HostSftpLogVO> querySftpLogPage(HostSftpLogQueryRequest request);
|
DataGrid<HostSftpLogVO> getHostSftpLogPage(HostSftpLogQueryRequest request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除 SFTP 操作日志
|
||||||
|
*
|
||||||
|
* @param idList idList
|
||||||
|
* @return effect
|
||||||
|
*/
|
||||||
|
Integer deleteHostSftpLog(List<Long> idList);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
package com.orion.ops.module.asset.service.impl;
|
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.define.wrapper.DataGrid;
|
||||||
import com.orion.lang.utils.Arrays1;
|
import com.orion.lang.utils.Arrays1;
|
||||||
import com.orion.lang.utils.Strings;
|
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.convert.HostSftpLogConvert;
|
||||||
import com.orion.ops.module.asset.define.operator.HostTerminalOperatorType;
|
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.request.host.HostSftpLogQueryRequest;
|
||||||
@@ -33,7 +37,49 @@ public class HostSftpLogServiceImpl implements HostSftpLogService {
|
|||||||
private OperatorLogApi operatorLogApi;
|
private OperatorLogApi operatorLogApi;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DataGrid<HostSftpLogVO> querySftpLogPage(HostSftpLogQueryRequest request) {
|
public DataGrid<HostSftpLogVO> getHostSftpLogPage(HostSftpLogQueryRequest request) {
|
||||||
|
// 查询
|
||||||
|
OperatorLogQueryDTO query = this.buildQueryInfo(request);
|
||||||
|
DataGrid<OperatorLogDTO> dataGrid = operatorLogApi.getOperatorLogPage(query);
|
||||||
|
// 转换
|
||||||
|
List<HostSftpLogVO> 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<HostSftpLogVO> 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<Long> 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();
|
Long hostId = request.getHostId();
|
||||||
String type = request.getType();
|
String type = request.getType();
|
||||||
// 构建参数
|
// 构建参数
|
||||||
@@ -55,19 +101,7 @@ public class HostSftpLogServiceImpl implements HostSftpLogService {
|
|||||||
if (hostId != null) {
|
if (hostId != null) {
|
||||||
query.setExtra("\"hostId\": " + hostId + ",");
|
query.setExtra("\"hostId\": " + hostId + ",");
|
||||||
}
|
}
|
||||||
// 查询
|
return query;
|
||||||
DataGrid<OperatorLogDTO> dataGrid = operatorLogApi.getOperatorLogList(query);
|
|
||||||
// 返回
|
|
||||||
DataGrid<HostSftpLogVO> result = new DataGrid<>();
|
|
||||||
List<HostSftpLogVO> rows = dataGrid.stream()
|
|
||||||
.map(HostSftpLogConvert.MAPPER::to)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
result.setRows(rows);
|
|
||||||
result.setPage(dataGrid.getPage());
|
|
||||||
result.setLimit(dataGrid.getLimit());
|
|
||||||
result.setSize(dataGrid.getSize());
|
|
||||||
result.setTotal(dataGrid.getTotal());
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ 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.OperatorLogDTO;
|
||||||
import com.orion.ops.module.infra.entity.dto.operator.OperatorLogQueryDTO;
|
import com.orion.ops.module.infra.entity.dto.operator.OperatorLogQueryDTO;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 操作日志服务
|
* 操作日志服务
|
||||||
*
|
*
|
||||||
@@ -14,11 +16,19 @@ import com.orion.ops.module.infra.entity.dto.operator.OperatorLogQueryDTO;
|
|||||||
public interface OperatorLogApi {
|
public interface OperatorLogApi {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 操作日志服务
|
* 分页查询操作日志
|
||||||
*
|
*
|
||||||
* @param request request
|
* @param request request
|
||||||
* @return rows
|
* @return rows
|
||||||
*/
|
*/
|
||||||
DataGrid<OperatorLogDTO> getOperatorLogList(OperatorLogQueryDTO request);
|
DataGrid<OperatorLogDTO> getOperatorLogPage(OperatorLogQueryDTO request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除操作日志
|
||||||
|
*
|
||||||
|
* @param idList idList
|
||||||
|
* @return effect
|
||||||
|
*/
|
||||||
|
Integer deleteOperatorLog(List<Long> idList);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 操作日志服务实现
|
* 操作日志服务实现
|
||||||
@@ -29,7 +30,7 @@ public class OperatorLogApiImpl implements OperatorLogApi {
|
|||||||
private OperatorLogDAO operatorLogDAO;
|
private OperatorLogDAO operatorLogDAO;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DataGrid<OperatorLogDTO> getOperatorLogList(OperatorLogQueryDTO request) {
|
public DataGrid<OperatorLogDTO> getOperatorLogPage(OperatorLogQueryDTO request) {
|
||||||
Valid.valid(request);
|
Valid.valid(request);
|
||||||
return operatorLogDAO.of()
|
return operatorLogDAO.of()
|
||||||
.page(request)
|
.page(request)
|
||||||
@@ -37,6 +38,11 @@ public class OperatorLogApiImpl implements OperatorLogApi {
|
|||||||
.dataGrid(OperatorLogProviderConvert.MAPPER::to);
|
.dataGrid(OperatorLogProviderConvert.MAPPER::to);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer deleteOperatorLog(List<Long> idList) {
|
||||||
|
return operatorLogDAO.deleteBatchIds(idList);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构建查询 wrapper
|
* 构建查询 wrapper
|
||||||
*
|
*
|
||||||
|
|||||||
62
orion-ops-ui/src/api/asset/host-sftp-log.ts
Normal file
62
orion-ops-ui/src/api/asset/host-sftp-log.ts
Normal file
@@ -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<DataGrid<HostSftpLogQueryResponse>>('/asset/host-sftp-log/query', request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除 SFTP 操作日志
|
||||||
|
*/
|
||||||
|
export function deleteHostSftpLog(idList: Array<number>) {
|
||||||
|
return axios.delete('/asset/host-sftp-log/delete', {
|
||||||
|
params: { idList },
|
||||||
|
paramsSerializer: params => {
|
||||||
|
return qs.stringify(params, { arrayFormat: 'comma' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -230,6 +230,20 @@ body {
|
|||||||
margin-bottom: 16px;
|
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 {
|
.copy-left, .copy-right {
|
||||||
color: rgb(var(--arcoblue-6));
|
color: rgb(var(--arcoblue-6));
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|||||||
@@ -12,6 +12,11 @@ const HOST_AUDIT: AppRouteRecordRaw =
|
|||||||
path: '/host-audit/connect-log',
|
path: '/host-audit/connect-log',
|
||||||
component: () => import('@/views/host-audit/connect-log/index.vue'),
|
component: () => import('@/views/host-audit/connect-log/index.vue'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'hostAuditSftpLog',
|
||||||
|
path: '/host-audit/sftp-log',
|
||||||
|
component: () => import('@/views/host-audit/sftp-log/index.vue'),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -23,10 +23,11 @@
|
|||||||
<a-descriptions-item label="连接主机">
|
<a-descriptions-item label="连接主机">
|
||||||
<span>({{ record.hostId }}) {{ record.hostName }}</span>
|
<span>({{ record.hostId }}) {{ record.hostName }}</span>
|
||||||
<br>
|
<br>
|
||||||
<span class="copy-left" title="复制" @click="copy(record.hostAddress)">
|
<span class="host-address text-copy"
|
||||||
<icon-copy />
|
:title="record.hostAddress"
|
||||||
|
@click="copy(record.hostAddress)">
|
||||||
|
{{ record.hostAddress }}
|
||||||
</span>
|
</span>
|
||||||
<span class="host-address">{{ record.hostAddress }}</span>
|
|
||||||
</a-descriptions-item>
|
</a-descriptions-item>
|
||||||
<!-- 连接类型 -->
|
<!-- 连接类型 -->
|
||||||
<a-descriptions-item label="连接类型">
|
<a-descriptions-item label="连接类型">
|
||||||
@@ -36,14 +37,15 @@
|
|||||||
<a-descriptions-item label="连接状态">
|
<a-descriptions-item label="连接状态">
|
||||||
{{ getDictValue(connectStatusKey, record.status) }}
|
{{ getDictValue(connectStatusKey, record.status) }}
|
||||||
</a-descriptions-item>
|
</a-descriptions-item>
|
||||||
<!-- 连接地址 -->
|
<!-- 留痕地址 -->
|
||||||
<a-descriptions-item label="连接地址">
|
<a-descriptions-item label="留痕地址">
|
||||||
<span>{{ record.extra?.location }}</span>
|
<span>{{ record.extra?.location }}</span>
|
||||||
<br>
|
<br>
|
||||||
<span class="copy-left" title="复制" @click="copy(record.extra?.address)">
|
<span class="connect-address text-copy"
|
||||||
<icon-copy />
|
:title="record.extra?.address"
|
||||||
|
@click="copy(record.extra?.address)">
|
||||||
|
{{ record.extra?.address }}
|
||||||
</span>
|
</span>
|
||||||
<span class="connect-address">{{ record.extra?.address }}</span>
|
|
||||||
</a-descriptions-item>
|
</a-descriptions-item>
|
||||||
<!-- userAgent -->
|
<!-- userAgent -->
|
||||||
<a-descriptions-item label="userAgent">
|
<a-descriptions-item label="userAgent">
|
||||||
@@ -63,15 +65,21 @@
|
|||||||
</a-descriptions-item>
|
</a-descriptions-item>
|
||||||
<!-- traceId -->
|
<!-- traceId -->
|
||||||
<a-descriptions-item label="traceId">
|
<a-descriptions-item label="traceId">
|
||||||
{{ record.extra?.traceId }}
|
<span class="text-copy" @click="copy(record.extra?.traceId)">
|
||||||
|
{{ record.extra?.traceId }}
|
||||||
|
</span>
|
||||||
</a-descriptions-item>
|
</a-descriptions-item>
|
||||||
<!-- channelId -->
|
<!-- channelId -->
|
||||||
<a-descriptions-item label="channelId">
|
<a-descriptions-item label="channelId">
|
||||||
{{ record.extra?.channelId }}
|
<span class="text-copy" @click="copy(record.extra?.channelId)">
|
||||||
|
{{ record.extra?.channelId }}
|
||||||
|
</span>
|
||||||
</a-descriptions-item>
|
</a-descriptions-item>
|
||||||
<!-- sessionId -->
|
<!-- sessionId -->
|
||||||
<a-descriptions-item label="sessionId">
|
<a-descriptions-item label="sessionId">
|
||||||
{{ record.extra?.sessionId }}
|
<span class="text-copy" @click="copy(record.extra?.sessionId)">
|
||||||
|
{{ record.extra?.sessionId }}
|
||||||
|
</span>
|
||||||
</a-descriptions-item>
|
</a-descriptions-item>
|
||||||
</a-descriptions>
|
</a-descriptions>
|
||||||
</a-drawer>
|
</a-drawer>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<a-card class="general-card table-search-card">
|
<a-card class="general-card table-search-card">
|
||||||
<query-header :model="formModel"
|
<query-header :model="formModel"
|
||||||
label-align="left"
|
label-align="left"
|
||||||
:itemOptions="{ 6: { span: 2 } }"
|
:itemOptions="{ 5: { span: 2 } }"
|
||||||
@submit="fetchTableData"
|
@submit="fetchTableData"
|
||||||
@reset="fetchTableData"
|
@reset="fetchTableData"
|
||||||
@keyup.enter="() => fetchTableData()">
|
@keyup.enter="() => fetchTableData()">
|
||||||
@@ -99,19 +99,6 @@
|
|||||||
@page-change="(page) => fetchTableData(page, pagination.pageSize)"
|
@page-change="(page) => fetchTableData(page, pagination.pageSize)"
|
||||||
@page-size-change="(size) => fetchTableData(1, size)"
|
@page-size-change="(size) => fetchTableData(1, size)"
|
||||||
:bordered="false">
|
:bordered="false">
|
||||||
<!-- 连接地址 -->
|
|
||||||
<template #address="{ record }">
|
|
||||||
<span class="connect-location" :title="record.extra?.location">
|
|
||||||
{{ record.extra?.location }}
|
|
||||||
</span>
|
|
||||||
<br>
|
|
||||||
<span class="copy-left" title="复制" @click="copy(record.extra?.address)">
|
|
||||||
<icon-copy />
|
|
||||||
</span>
|
|
||||||
<span class="connect-address" :title="record.extra?.address">
|
|
||||||
{{ record.extra?.address }}
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
<!-- 连接用户 -->
|
<!-- 连接用户 -->
|
||||||
<template #username="{ record }">
|
<template #username="{ record }">
|
||||||
{{ record.username }}
|
{{ record.username }}
|
||||||
@@ -122,10 +109,9 @@
|
|||||||
{{ record.hostName }}
|
{{ record.hostName }}
|
||||||
</span>
|
</span>
|
||||||
<br>
|
<br>
|
||||||
<span class="copy-left" title="复制" @click="copy(record.hostAddress)">
|
<span class="host-address text-copy"
|
||||||
<icon-copy />
|
:title="record.hostAddress"
|
||||||
</span>
|
@click="copy(record.hostAddress)">
|
||||||
<span class="host-address" :title="record.hostAddress">
|
|
||||||
{{ record.hostAddress }}
|
{{ record.hostAddress }}
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
@@ -136,6 +122,18 @@
|
|||||||
}" />
|
}" />
|
||||||
{{ getDictValue(connectStatusKey, record.status) }}
|
{{ getDictValue(connectStatusKey, record.status) }}
|
||||||
</template>
|
</template>
|
||||||
|
<!-- 留痕地址 -->
|
||||||
|
<template #address="{ record }">
|
||||||
|
<span class="connect-location" :title="record.extra?.location">
|
||||||
|
{{ record.extra?.location }}
|
||||||
|
</span>
|
||||||
|
<br>
|
||||||
|
<span class="connect-address text-copy"
|
||||||
|
:title="record.extra?.address"
|
||||||
|
@click="copy(record.extra?.address)">
|
||||||
|
{{ record.extra?.address }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
<!-- 操作 -->
|
<!-- 操作 -->
|
||||||
<template #handle="{ record }">
|
<template #handle="{ record }">
|
||||||
<div class="table-handle-wrapper">
|
<div class="table-handle-wrapper">
|
||||||
@@ -203,8 +201,6 @@
|
|||||||
import ConnectLogClearModal from './connect-log-clear-modal.vue';
|
import ConnectLogClearModal from './connect-log-clear-modal.vue';
|
||||||
import ConnectLogDetailDrawer from './connect-log-detail-drawer.vue';
|
import ConnectLogDetailDrawer from './connect-log-detail-drawer.vue';
|
||||||
|
|
||||||
const emits = defineEmits(['openAdd', 'openUpdate']);
|
|
||||||
|
|
||||||
const tableRenderData = ref<HostConnectLogQueryResponse[]>([]);
|
const tableRenderData = ref<HostConnectLogQueryResponse[]>([]);
|
||||||
const selectedKeys = ref<number[]>([]);
|
const selectedKeys = ref<number[]>([]);
|
||||||
const clearModal = ref();
|
const clearModal = ref();
|
||||||
|
|||||||
@@ -3,17 +3,10 @@ import { dateFormat } from '@/utils';
|
|||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
title: '连接地址',
|
|
||||||
dataIndex: 'address',
|
|
||||||
slotName: 'address',
|
|
||||||
width: 156,
|
|
||||||
align: 'left',
|
|
||||||
ellipsis: true,
|
|
||||||
}, {
|
|
||||||
title: '连接用户',
|
title: '连接用户',
|
||||||
dataIndex: 'username',
|
dataIndex: 'username',
|
||||||
slotName: 'username',
|
slotName: 'username',
|
||||||
width: 180,
|
width: 140,
|
||||||
align: 'left',
|
align: 'left',
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
}, {
|
}, {
|
||||||
@@ -34,6 +27,13 @@ const columns = [
|
|||||||
slotName: 'status',
|
slotName: 'status',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
width: 106,
|
width: 106,
|
||||||
|
}, {
|
||||||
|
title: '留痕地址',
|
||||||
|
dataIndex: 'address',
|
||||||
|
slotName: 'address',
|
||||||
|
width: 156,
|
||||||
|
align: 'left',
|
||||||
|
ellipsis: true,
|
||||||
}, {
|
}, {
|
||||||
title: '连接时间',
|
title: '连接时间',
|
||||||
dataIndex: 'connectTime',
|
dataIndex: 'connectTime',
|
||||||
|
|||||||
@@ -0,0 +1,286 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 搜索 -->
|
||||||
|
<a-card class="general-card table-search-card">
|
||||||
|
<query-header :model="formModel"
|
||||||
|
label-align="left"
|
||||||
|
:itemOptions="{ 4: { span: 2 } }"
|
||||||
|
@submit="fetchTableData"
|
||||||
|
@reset="fetchTableData"
|
||||||
|
@keyup.enter="() => fetchTableData()">
|
||||||
|
<!-- 操作用户 -->
|
||||||
|
<a-form-item field="userId" label="操作用户" label-col-flex="50px">
|
||||||
|
<user-selector v-model="formModel.userId"
|
||||||
|
placeholder="请选择用户"
|
||||||
|
allow-clear />
|
||||||
|
</a-form-item>
|
||||||
|
<!-- 操作主机 -->
|
||||||
|
<a-form-item field="hostId" label="操作主机" label-col-flex="50px">
|
||||||
|
<host-selector v-model="formModel.hostId"
|
||||||
|
placeholder="请选择主机"
|
||||||
|
allow-clear />
|
||||||
|
</a-form-item>
|
||||||
|
<!-- 操作类型 -->
|
||||||
|
<a-form-item field="type" label="操作类型" label-col-flex="50px">
|
||||||
|
<a-select v-model="formModel.type"
|
||||||
|
placeholder="请选择类型"
|
||||||
|
:options="toOptions(sftpOperatorTypeKey)"
|
||||||
|
allow-clear />
|
||||||
|
</a-form-item>
|
||||||
|
<!-- 执行结果 -->
|
||||||
|
<a-form-item field="result" label="执行结果" label-col-flex="50px">
|
||||||
|
<a-select v-model="formModel.result"
|
||||||
|
placeholder="请选择执行结果"
|
||||||
|
:options="toOptions(sftpOperatorResultKey)"
|
||||||
|
allow-clear />
|
||||||
|
</a-form-item>
|
||||||
|
<!-- 开始时间 -->
|
||||||
|
<a-form-item field="startTimeRange" label="开始时间" label-col-flex="50px">
|
||||||
|
<a-range-picker v-model="formModel.startTimeRange"
|
||||||
|
style="width: 100%"
|
||||||
|
:time-picker-props="{ defaultValue: ['00:00:00', '23:59:59'] }"
|
||||||
|
show-time
|
||||||
|
format="YYYY-MM-DD HH:mm:ss" />
|
||||||
|
</a-form-item>
|
||||||
|
</query-header>
|
||||||
|
</a-card>
|
||||||
|
<!-- 表格 -->
|
||||||
|
<a-card class="general-card table-card">
|
||||||
|
<template #title>
|
||||||
|
<!-- 左侧操作 -->
|
||||||
|
<div class="table-left-bar-handle">
|
||||||
|
<!-- 标题 -->
|
||||||
|
<div class="table-title">
|
||||||
|
SFTP 操作日志
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 右侧操作 -->
|
||||||
|
<div class="table-right-bar-handle">
|
||||||
|
<a-space>
|
||||||
|
<!-- 删除 -->
|
||||||
|
<a-popconfirm :content="`确认删除选中的 ${selectedKeys.length} 条记录吗?`"
|
||||||
|
position="br"
|
||||||
|
type="warning"
|
||||||
|
@ok="deleteSelectRows">
|
||||||
|
<a-button v-permission="['infra:operator-log:delete', 'asset:host-sftp-log:management:delete']"
|
||||||
|
type="secondary"
|
||||||
|
status="danger"
|
||||||
|
:disabled="selectedKeys.length === 0">
|
||||||
|
删除
|
||||||
|
<template #icon>
|
||||||
|
<icon-delete />
|
||||||
|
</template>
|
||||||
|
</a-button>
|
||||||
|
</a-popconfirm>
|
||||||
|
</a-space>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<!-- table -->
|
||||||
|
<a-table row-key="id"
|
||||||
|
ref="tableRef"
|
||||||
|
:loading="loading"
|
||||||
|
v-model:selected-keys="selectedKeys"
|
||||||
|
:row-selection="rowSelection"
|
||||||
|
:columns="columns"
|
||||||
|
:data="tableRenderData"
|
||||||
|
:pagination="pagination"
|
||||||
|
@page-change="(page) => fetchTableData(page, pagination.pageSize)"
|
||||||
|
@page-size-change="(size) => fetchTableData(1, size)"
|
||||||
|
:bordered="false">
|
||||||
|
<!-- 操作用户 -->
|
||||||
|
<template #username="{ record }">
|
||||||
|
{{ record.username }}
|
||||||
|
</template>
|
||||||
|
<!-- 操作主机 -->
|
||||||
|
<template #hostName="{ record }">
|
||||||
|
<span class="host-name" :title="record.hostName">
|
||||||
|
{{ record.hostName }}
|
||||||
|
</span>
|
||||||
|
<br>
|
||||||
|
<span class="host-address text-copy"
|
||||||
|
:title="record.hostAddress"
|
||||||
|
@click="copy(record.hostAddress)">
|
||||||
|
{{ record.hostAddress }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<!-- 操作类型 -->
|
||||||
|
<template #type="{ record }">
|
||||||
|
{{ getDictValue(sftpOperatorTypeKey, record.type) }}
|
||||||
|
</template>
|
||||||
|
<!-- 操作文件 -->
|
||||||
|
<template #paths="{ record }">
|
||||||
|
<div class="paths-wrapper">
|
||||||
|
<span v-for="path in record.paths"
|
||||||
|
class="path-wrapper text-ellipsis text-copy"
|
||||||
|
:title="path"
|
||||||
|
@click="copy(path)">
|
||||||
|
{{ path }}
|
||||||
|
</span>
|
||||||
|
<!-- 移动目标路径 -->
|
||||||
|
<span class="sub-text" v-if="SftpOperatorType.SFTP_MOVE === record.type">
|
||||||
|
移动到 {{ record.extra?.target }}
|
||||||
|
</span>
|
||||||
|
<!-- 提权信息 -->
|
||||||
|
<span class="sub-text" v-if="SftpOperatorType.SFTP_CHMOD === record.type">
|
||||||
|
提权 {{ record.extra?.mod }} {{ permission10toString(record.extra?.mod as number) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<!-- 执行结果 -->
|
||||||
|
<template #result="{ record }">
|
||||||
|
<a-tag :color="getDictValue(sftpOperatorResultKey, record.result, 'color')">
|
||||||
|
{{ getDictValue(sftpOperatorResultKey, record.result) }}
|
||||||
|
</a-tag>
|
||||||
|
</template>
|
||||||
|
<!-- 留痕地址 -->
|
||||||
|
<template #address="{ record }">
|
||||||
|
<span class="operator-location" :title="record.location">
|
||||||
|
{{ record.location }}
|
||||||
|
</span>
|
||||||
|
<br>
|
||||||
|
<span class="operator-address text-copy"
|
||||||
|
:title="record.address"
|
||||||
|
@click="copy(record.address)">
|
||||||
|
{{ record.address }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<!-- 操作 -->
|
||||||
|
<template #handle="{ record }">
|
||||||
|
<div class="table-handle-wrapper">
|
||||||
|
<!-- 删除 -->
|
||||||
|
<a-popconfirm content="确认删除这条记录吗?"
|
||||||
|
position="left"
|
||||||
|
type="warning"
|
||||||
|
@ok="deleteRow(record)">
|
||||||
|
<a-button v-permission="['infra:operator-log:delete', 'asset:host-sftp-log:management:delete']"
|
||||||
|
type="text"
|
||||||
|
size="mini"
|
||||||
|
status="danger">
|
||||||
|
删除
|
||||||
|
</a-button>
|
||||||
|
</a-popconfirm>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</a-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: 'hostAuditSftpLogTable'
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { HostSftpLogQueryRequest, HostSftpLogQueryResponse } from '@/api/asset/host-sftp-log';
|
||||||
|
import { reactive, ref, onMounted } from 'vue';
|
||||||
|
import { getHostSftpLogPage, deleteHostSftpLog } from '@/api/asset/host-sftp-log';
|
||||||
|
import { sftpOperatorTypeKey, sftpOperatorResultKey, SftpOperatorType } from '../types/const';
|
||||||
|
import { usePagination, useRowSelection } from '@/types/table';
|
||||||
|
import { useDictStore } from '@/store';
|
||||||
|
import { Message } from '@arco-design/web-vue';
|
||||||
|
import columns from '../types/table.columns';
|
||||||
|
import useLoading from '@/hooks/loading';
|
||||||
|
import useCopy from '@/hooks/copy';
|
||||||
|
import UserSelector from '@/components/user/user/user-selector.vue';
|
||||||
|
import HostSelector from '@/components/asset/host/host-selector.vue';
|
||||||
|
import { permission10toString } from '@/utils/file';
|
||||||
|
|
||||||
|
const tableRenderData = ref<HostSftpLogQueryResponse[]>([]);
|
||||||
|
const selectedKeys = ref<number[]>([]);
|
||||||
|
|
||||||
|
const pagination = usePagination();
|
||||||
|
const rowSelection = useRowSelection();
|
||||||
|
const { loading, setLoading } = useLoading();
|
||||||
|
const { toOptions, getDictValue } = useDictStore();
|
||||||
|
const { copy } = useCopy();
|
||||||
|
|
||||||
|
const formModel = reactive<HostSftpLogQueryRequest>({
|
||||||
|
userId: undefined,
|
||||||
|
hostId: undefined,
|
||||||
|
type: undefined,
|
||||||
|
result: undefined,
|
||||||
|
startTimeRange: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 加载数据
|
||||||
|
const doFetchTableData = async (request: HostSftpLogQueryRequest) => {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
const { data } = await getHostSftpLogPage(request);
|
||||||
|
tableRenderData.value = data.rows;
|
||||||
|
pagination.total = data.total;
|
||||||
|
pagination.current = request.page;
|
||||||
|
pagination.pageSize = request.limit;
|
||||||
|
selectedKeys.value = [];
|
||||||
|
} catch (e) {
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 切换页码
|
||||||
|
const fetchTableData = (page = 1, limit = pagination.pageSize, form = formModel) => {
|
||||||
|
doFetchTableData({ page, limit, ...form });
|
||||||
|
};
|
||||||
|
|
||||||
|
// 删除选中行
|
||||||
|
const deleteSelectRows = async () => {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
// 调用删除接口
|
||||||
|
await deleteHostSftpLog(selectedKeys.value);
|
||||||
|
Message.success(`成功删除 ${selectedKeys.value.length} 条数据`);
|
||||||
|
selectedKeys.value = [];
|
||||||
|
// 重新加载数据
|
||||||
|
fetchTableData();
|
||||||
|
} catch (e) {
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 删除当前行
|
||||||
|
const deleteRow = async ({ id }: {
|
||||||
|
id: number
|
||||||
|
}) => {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
// 调用删除接口
|
||||||
|
await deleteHostSftpLog([id]);
|
||||||
|
Message.success('删除成功');
|
||||||
|
selectedKeys.value = [];
|
||||||
|
// 重新加载数据
|
||||||
|
fetchTableData();
|
||||||
|
} catch (e) {
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fetchTableData();
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.host-name, .operator-location {
|
||||||
|
color: var(--color-text-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.host-address, .operator-address, .sub-text {
|
||||||
|
margin-top: 4px;
|
||||||
|
display: inline-block;
|
||||||
|
color: var(--color-text-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.paths-wrapper {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.path-wrapper {
|
||||||
|
display: block;
|
||||||
|
padding: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
39
orion-ops-ui/src/views/host-audit/sftp-log/index.vue
Normal file
39
orion-ops-ui/src/views/host-audit/sftp-log/index.vue
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<template>
|
||||||
|
<div class="layout-container" v-if="render">
|
||||||
|
<!-- 列表-表格 -->
|
||||||
|
<sftp-log-table />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: 'hostAuditSftpLog'
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, onBeforeMount, onUnmounted } from 'vue';
|
||||||
|
import { useCacheStore, useDictStore } from '@/store';
|
||||||
|
import { dictKeys } from './types/const';
|
||||||
|
import SftpLogTable from './components/sftp-log-table.vue';
|
||||||
|
|
||||||
|
const render = ref(false);
|
||||||
|
|
||||||
|
// 加载字典配置
|
||||||
|
onBeforeMount(async () => {
|
||||||
|
const dictStore = useDictStore();
|
||||||
|
await dictStore.loadKeys(dictKeys);
|
||||||
|
render.value = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 重置缓存
|
||||||
|
onUnmounted(() => {
|
||||||
|
const cacheStore = useCacheStore();
|
||||||
|
cacheStore.reset('users', 'hosts');
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
14
orion-ops-ui/src/views/host-audit/sftp-log/types/const.ts
Normal file
14
orion-ops-ui/src/views/host-audit/sftp-log/types/const.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
// sftp 操作类型
|
||||||
|
export const SftpOperatorType = {
|
||||||
|
SFTP_MOVE: 'host-terminal:sftp-move',
|
||||||
|
SFTP_CHMOD: 'host-terminal:sftp-chmod',
|
||||||
|
};
|
||||||
|
|
||||||
|
// sftp 操作类型 字典项
|
||||||
|
export const sftpOperatorTypeKey = 'sftpOperatorType';
|
||||||
|
|
||||||
|
// sftp 操作结果 字典项
|
||||||
|
export const sftpOperatorResultKey = 'operatorLogResult';
|
||||||
|
|
||||||
|
// 加载的字典值
|
||||||
|
export const dictKeys = [sftpOperatorTypeKey, sftpOperatorResultKey];
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
import type { TableColumnData } from '@arco-design/web-vue/es/table/interface';
|
||||||
|
import { dateFormat } from '@/utils';
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: '操作用户',
|
||||||
|
dataIndex: 'username',
|
||||||
|
slotName: 'username',
|
||||||
|
width: 140,
|
||||||
|
align: 'left',
|
||||||
|
ellipsis: true,
|
||||||
|
}, {
|
||||||
|
title: '操作主机',
|
||||||
|
dataIndex: 'hostName',
|
||||||
|
slotName: 'hostName',
|
||||||
|
width: 180,
|
||||||
|
align: 'left',
|
||||||
|
ellipsis: true,
|
||||||
|
}, {
|
||||||
|
title: '操作类型',
|
||||||
|
dataIndex: 'type',
|
||||||
|
slotName: 'type',
|
||||||
|
width: 116,
|
||||||
|
align: 'left',
|
||||||
|
}, {
|
||||||
|
title: '操作文件',
|
||||||
|
dataIndex: 'paths',
|
||||||
|
slotName: 'paths',
|
||||||
|
align: 'left',
|
||||||
|
}, {
|
||||||
|
title: '执行结果',
|
||||||
|
dataIndex: 'result',
|
||||||
|
slotName: 'result',
|
||||||
|
align: 'left',
|
||||||
|
width: 88,
|
||||||
|
}, {
|
||||||
|
title: '留痕地址',
|
||||||
|
dataIndex: 'address',
|
||||||
|
slotName: 'address',
|
||||||
|
width: 156,
|
||||||
|
align: 'left',
|
||||||
|
ellipsis: true,
|
||||||
|
}, {
|
||||||
|
title: '操作时间',
|
||||||
|
dataIndex: 'startTime',
|
||||||
|
slotName: 'startTime',
|
||||||
|
align: 'center',
|
||||||
|
width: 180,
|
||||||
|
render: ({ record }) => {
|
||||||
|
return (record.startTime && dateFormat(new Date(record.startTime)));
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
title: '操作',
|
||||||
|
slotName: 'handle',
|
||||||
|
width: 80,
|
||||||
|
align: 'center',
|
||||||
|
fixed: 'right',
|
||||||
|
},
|
||||||
|
] as TableColumnData[];
|
||||||
|
|
||||||
|
export default columns;
|
||||||
@@ -32,16 +32,15 @@
|
|||||||
<icon-copy class="copy-left" @click="copy(record.originLogInfo, '已复制')" />
|
<icon-copy class="copy-left" @click="copy(record.originLogInfo, '已复制')" />
|
||||||
<span v-html="replaceHtmlTag(record.logInfo)" />
|
<span v-html="replaceHtmlTag(record.logInfo)" />
|
||||||
</template>
|
</template>
|
||||||
<!-- 操作地址 -->
|
<!-- 留痕地址 -->
|
||||||
<template #address="{ record }">
|
<template #address="{ record }">
|
||||||
<span class="operator-location" :title="record.location">
|
<span class="operator-location" :title="record.location">
|
||||||
{{ record.location }}
|
{{ record.location }}
|
||||||
</span>
|
</span>
|
||||||
<br>
|
<br>
|
||||||
<span class="copy-left" title="复制" @click="copy(record.address)">
|
<span class="operator-address text-copy"
|
||||||
<icon-copy />
|
:title="record.address"
|
||||||
</span>
|
@click="copy(record.address)">
|
||||||
<span class="operator-address" :title="record.address">
|
|
||||||
{{ record.address }}
|
{{ record.address }}
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -79,16 +79,15 @@
|
|||||||
<icon-copy class="copy-left" @click="copy(record.originLogInfo, '已复制')" />
|
<icon-copy class="copy-left" @click="copy(record.originLogInfo, '已复制')" />
|
||||||
<span v-html="replaceHtmlTag(record.logInfo)" />
|
<span v-html="replaceHtmlTag(record.logInfo)" />
|
||||||
</template>
|
</template>
|
||||||
<!-- 操作地址 -->
|
<!-- 留痕地址 -->
|
||||||
<template #address="{ record }">
|
<template #address="{ record }">
|
||||||
<span class="operator-location" :title="record.location">
|
<span class="operator-location" :title="record.location">
|
||||||
{{ record.location }}
|
{{ record.location }}
|
||||||
</span>
|
</span>
|
||||||
<br>
|
<br>
|
||||||
<span class="copy-left" title="复制" @click="copy(record.address)">
|
<span class="operator-address text-copy"
|
||||||
<icon-copy />
|
:title="record.address"
|
||||||
</span>
|
@click="copy(record.address)">
|
||||||
<span class="operator-address" :title="record.address">
|
|
||||||
{{ record.address }}
|
{{ record.address }}
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ const columns = [
|
|||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
tooltip: true,
|
tooltip: true,
|
||||||
}, {
|
}, {
|
||||||
title: '操作地址',
|
title: '留痕地址',
|
||||||
dataIndex: 'address',
|
dataIndex: 'address',
|
||||||
slotName: 'address',
|
slotName: 'address',
|
||||||
width: 156,
|
width: 156,
|
||||||
|
|||||||
Reference in New Issue
Block a user