Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5113aa63bd | ||
|
|
518fd8c839 | ||
|
|
a046faaa07 | ||
|
|
dcf25392ff | ||
|
|
7f24948efa | ||
|
|
59d9739f36 | ||
|
|
26a6d08d96 | ||
|
|
cd59c51344 |
@@ -1,7 +1,7 @@
|
||||
version: '3.3'
|
||||
services:
|
||||
orion-visor-service:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-service:2.0.4
|
||||
image: registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-service:2.0.5
|
||||
ports:
|
||||
- 1081:80
|
||||
environment:
|
||||
@@ -20,7 +20,7 @@ services:
|
||||
- orion-visor-mysql
|
||||
- orion-visor-redis
|
||||
orion-visor-mysql:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-mysql:2.0.4
|
||||
image: registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-mysql:2.0.5
|
||||
privileged: true
|
||||
ports:
|
||||
- 3307:3306
|
||||
@@ -34,7 +34,7 @@ services:
|
||||
- /data/orion-visor-space/docker-volumes/orion-visor-mysql/var-lib-mysql-files:/var/lib/mysql-files
|
||||
- /data/orion-visor-space/docker-volumes/orion-visor-mysql/etc-mysql:/etc/mysql
|
||||
orion-visor-redis:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-redis:2.0.4
|
||||
image: registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-redis:2.0.5
|
||||
privileged: true
|
||||
ports:
|
||||
- 6380:6379
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#/bin/bash
|
||||
version=2.0.4
|
||||
version=2.0.5
|
||||
cp -r ../../sql ./sql
|
||||
docker build -t orion-visor-mysql:${version} .
|
||||
rm -rf ./sql
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#/bin/bash
|
||||
version=2.0.4
|
||||
version=2.0.5
|
||||
docker build -t orion-visor-redis:${version} .
|
||||
docker tag orion-visor-redis:${version} registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-redis:${version}
|
||||
docker push registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-redis:${version}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#/bin/bash
|
||||
version=2.0.4
|
||||
version=2.0.5
|
||||
mv ../../orion-visor-launch/target/orion-visor-launch.jar ./orion-visor-launch.jar
|
||||
mv ../../orion-visor-ui/dist ./dist
|
||||
docker build -t orion-visor-service:${version} .
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<url>https://github.com/lijiahangmax/orion-visor</url>
|
||||
|
||||
<properties>
|
||||
<revision>2.0.4</revision>
|
||||
<revision>2.0.5</revision>
|
||||
<spring.boot.version>2.7.17</spring.boot.version>
|
||||
<spring.boot.admin.version>2.7.15</spring.boot.admin.version>
|
||||
<flatten.maven.plugin.version>1.5.0</flatten.maven.plugin.version>
|
||||
|
||||
@@ -14,7 +14,7 @@ public interface AppConst extends OrionConst {
|
||||
/**
|
||||
* 同 ${orion.version} 迭代时候需要手动更改
|
||||
*/
|
||||
String VERSION = "2.0.4";
|
||||
String VERSION = "2.0.5";
|
||||
|
||||
String ORION_VISOR = "orion-visor";
|
||||
|
||||
|
||||
@@ -97,4 +97,6 @@ public interface ErrorMessage {
|
||||
|
||||
String PLEASE_CHECK_HOST_SSH = "请检查主机 {} 是否存在/权限/SSH配置";
|
||||
|
||||
String CLIENT_ABORT = "手动中断";
|
||||
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.orion.visor.framework.redis.configuration.config.RedissonConfig;
|
||||
import com.orion.visor.framework.redis.core.lock.RedisLocker;
|
||||
import com.orion.visor.framework.redis.core.utils.RedisUtils;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.redisson.config.SingleServerConfig;
|
||||
import org.redisson.spring.starter.RedissonAutoConfigurationCustomizer;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
|
||||
@@ -58,6 +59,9 @@ public class OrionRedisAutoConfiguration {
|
||||
return config -> {
|
||||
config.setThreads(redissonConfig.getThreads());
|
||||
config.setNettyThreads(redissonConfig.getNettyThreads());
|
||||
// 单机配置
|
||||
SingleServerConfig single = config.useSingleServer();
|
||||
single.setConnectionMinimumIdleSize(redissonConfig.getMinimumIdleSize());
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -24,9 +24,15 @@ public class RedissonConfig {
|
||||
*/
|
||||
private Integer nettyThreads;
|
||||
|
||||
/**
|
||||
* 最小空闲连接数
|
||||
*/
|
||||
private Integer minimumIdleSize;
|
||||
|
||||
public RedissonConfig() {
|
||||
this.threads = 16;
|
||||
this.nettyThreads = 16;
|
||||
this.minimumIdleSize = 16;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,6 +18,12 @@
|
||||
"type": "java.lang.Integer",
|
||||
"description": "netty 线程数.",
|
||||
"defaultValue": "16"
|
||||
},
|
||||
{
|
||||
"name": "spring.redisson.minimum-idle-size",
|
||||
"type": "java.lang.Integer",
|
||||
"description": "最小空闲连接数.",
|
||||
"defaultValue": "16"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -14,6 +14,7 @@ spring:
|
||||
redisson:
|
||||
threads: 2
|
||||
netty-threads: 2
|
||||
minimum-idle-size: 2
|
||||
|
||||
mybatis-plus:
|
||||
configuration:
|
||||
|
||||
@@ -24,6 +24,7 @@ spring:
|
||||
redisson:
|
||||
threads: 4
|
||||
netty-threads: 4
|
||||
minimum-idle-size: 4
|
||||
quartz:
|
||||
properties:
|
||||
org:
|
||||
|
||||
@@ -19,6 +19,9 @@ spring:
|
||||
mvc:
|
||||
pathmatch:
|
||||
matching-strategy: ANT_PATH_MATCHER
|
||||
async:
|
||||
# 异步请求时间 30min
|
||||
request-timeout: 1800000
|
||||
datasource:
|
||||
druid:
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
### 分页查询 SFTP 操作日志
|
||||
POST {{baseUrl}}/asset/host-sftp-log/query
|
||||
POST {{baseUrl}}/asset/host-sftp/query-log
|
||||
Content-Type: application/json
|
||||
Authorization: {{token}}
|
||||
|
||||
@@ -10,8 +10,12 @@ Authorization: {{token}}
|
||||
|
||||
|
||||
### 删除 SFTP 操作日志
|
||||
DELETE {{baseUrl}}/asset/host-sftp-log/delete?idList=1,2,3
|
||||
DELETE {{baseUrl}}/asset/host-sftp/delete-log?idList=1,2,3
|
||||
Authorization: {{token}}
|
||||
|
||||
|
||||
### 下载文件
|
||||
GET {{baseUrl}}/asset/host-sftp/download?channelId=123&transferToken=123
|
||||
|
||||
|
||||
###
|
||||
|
||||
@@ -5,11 +5,12 @@ import com.orion.visor.framework.biz.operator.log.core.annotation.OperatorLog;
|
||||
import com.orion.visor.framework.common.validator.group.Page;
|
||||
import com.orion.visor.framework.log.core.annotation.IgnoreLog;
|
||||
import com.orion.visor.framework.log.core.enums.IgnoreLogMode;
|
||||
import com.orion.visor.framework.web.core.annotation.IgnoreWrapper;
|
||||
import com.orion.visor.framework.web.core.annotation.RestWrapper;
|
||||
import com.orion.visor.module.asset.define.operator.HostTerminalOperatorType;
|
||||
import com.orion.visor.module.asset.entity.request.host.HostSftpLogQueryRequest;
|
||||
import com.orion.visor.module.asset.entity.vo.HostSftpLogVO;
|
||||
import com.orion.visor.module.asset.service.HostSftpLogService;
|
||||
import com.orion.visor.module.asset.service.HostSftpService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
@@ -17,44 +18,60 @@ 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 org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.annotation.security.PermitAll;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* SFTP 操作日志服务 api
|
||||
* SFTP 操作服务 api
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023-12-26 22:09
|
||||
*/
|
||||
@Tag(name = "asset - SFTP 操作日志服务")
|
||||
@Tag(name = "asset - SFTP 操作服务")
|
||||
@Slf4j
|
||||
@Validated
|
||||
@RestWrapper
|
||||
@RestController
|
||||
@RequestMapping("/asset/host-sftp-log")
|
||||
@RequestMapping("/asset/host-sftp")
|
||||
@SuppressWarnings({"ELValidationInJSP", "SpringElInspection"})
|
||||
public class HostSftpLogController {
|
||||
|
||||
@Resource
|
||||
private HostSftpLogService hostSftpLogService;
|
||||
private HostSftpService hostSftpService;
|
||||
|
||||
@IgnoreLog(IgnoreLogMode.RET)
|
||||
@PostMapping("/query")
|
||||
@PostMapping("/query-log")
|
||||
@Operation(summary = "分页查询 SFTP 操作日志")
|
||||
@PreAuthorize("@ss.hasAnyPermission('infra:operator-log:query', 'asset:host-sftp-log:management:query')")
|
||||
public DataGrid<HostSftpLogVO> getHostSftpLogPage(@Validated(Page.class) @RequestBody HostSftpLogQueryRequest request) {
|
||||
return hostSftpLogService.getHostSftpLogPage(request);
|
||||
return hostSftpService.getHostSftpLogPage(request);
|
||||
}
|
||||
|
||||
@OperatorLog(HostTerminalOperatorType.DELETE_SFTP_LOG)
|
||||
@DeleteMapping("/delete")
|
||||
@DeleteMapping("/delete-log")
|
||||
@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);
|
||||
return hostSftpService.deleteHostSftpLog(idList);
|
||||
}
|
||||
|
||||
@PermitAll
|
||||
@IgnoreWrapper
|
||||
@IgnoreLog(IgnoreLogMode.RET)
|
||||
@GetMapping("/download")
|
||||
@Operation(summary = "下载文件")
|
||||
@Parameter(name = "channelId", description = "channelId", required = true)
|
||||
@Parameter(name = "transferToken", description = "transferToken", required = true)
|
||||
public StreamingResponseBody downloadWithTransferToken(@RequestParam("channelId") String channelId,
|
||||
@RequestParam("transferToken") String transferToken,
|
||||
HttpServletResponse response) {
|
||||
return hostSftpService.downloadWithTransferToken(channelId, transferToken, response);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package com.orion.visor.module.asset.handler.host.terminal;
|
||||
|
||||
import com.orion.visor.module.asset.define.AssetThreadPools;
|
||||
import com.orion.visor.module.asset.handler.host.terminal.enums.InputTypeEnum;
|
||||
import com.orion.visor.module.asset.handler.host.terminal.manager.TerminalManager;
|
||||
import com.orion.visor.module.asset.handler.host.terminal.manager.HostTerminalManager;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.socket.CloseStatus;
|
||||
@@ -24,7 +24,7 @@ import javax.annotation.Resource;
|
||||
public class TerminalMessageDispatcher extends AbstractWebSocketHandler {
|
||||
|
||||
@Resource
|
||||
private TerminalManager terminalManager;
|
||||
private HostTerminalManager hostTerminalManager;
|
||||
|
||||
@Override
|
||||
protected void handleTextMessage(WebSocketSession session, TextMessage message) {
|
||||
@@ -65,7 +65,7 @@ public class TerminalMessageDispatcher extends AbstractWebSocketHandler {
|
||||
String id = session.getId();
|
||||
log.info("TerminalMessageDispatcher-afterConnectionClosed id: {}, code: {}, reason: {}", id, status.getCode(), status.getReason());
|
||||
// 关闭会话
|
||||
terminalManager.closeSession(id);
|
||||
hostTerminalManager.closeSession(id);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import com.orion.visor.framework.biz.operator.log.core.utils.OperatorLogs;
|
||||
import com.orion.visor.framework.common.constant.ErrorMessage;
|
||||
import com.orion.visor.framework.websocket.core.utils.WebSockets;
|
||||
import com.orion.visor.module.asset.handler.host.terminal.enums.OutputTypeEnum;
|
||||
import com.orion.visor.module.asset.handler.host.terminal.manager.TerminalManager;
|
||||
import com.orion.visor.module.asset.handler.host.terminal.manager.HostTerminalManager;
|
||||
import com.orion.visor.module.asset.handler.host.terminal.model.TerminalBasePayload;
|
||||
import com.orion.visor.module.asset.handler.host.terminal.model.TerminalConfig;
|
||||
import com.orion.visor.module.asset.handler.host.terminal.session.ITerminalSession;
|
||||
@@ -27,7 +27,7 @@ import java.util.Map;
|
||||
public abstract class AbstractTerminalHandler<T extends TerminalBasePayload> implements ITerminalHandler<T> {
|
||||
|
||||
@Resource
|
||||
protected TerminalManager terminalManager;
|
||||
protected HostTerminalManager hostTerminalManager;
|
||||
|
||||
@Resource
|
||||
private OperatorLogFrameworkService operatorLogFrameworkService;
|
||||
@@ -75,7 +75,7 @@ public abstract class AbstractTerminalHandler<T extends TerminalBasePayload> imp
|
||||
String channelId = channel.getId();
|
||||
String sessionId = payload.getSessionId();
|
||||
// 获取会话并且设置参数
|
||||
ITerminalSession session = terminalManager.getSession(channelId, sessionId);
|
||||
ITerminalSession session = hostTerminalManager.getSession(channelId, sessionId);
|
||||
if (session != null) {
|
||||
TerminalConfig config = session.getConfig();
|
||||
extra.put(OperatorLogs.HOST_ID, config.getHostId());
|
||||
|
||||
@@ -30,7 +30,7 @@ public class SftpChangeModHandler extends AbstractTerminalHandler<SftpChangeModR
|
||||
long startTime = System.currentTimeMillis();
|
||||
// 获取会话
|
||||
String sessionId = payload.getSessionId();
|
||||
ISftpSession session = terminalManager.getSession(channel.getId(), sessionId);
|
||||
ISftpSession session = hostTerminalManager.getSession(channel.getId(), sessionId);
|
||||
String path = payload.getPath();
|
||||
Integer mod = payload.getMod();
|
||||
log.info("SftpChangeModHandler-handle start sessionId: {}, path: {}, mod: {}", sessionId, path, mod);
|
||||
|
||||
@@ -30,7 +30,7 @@ public class SftpDownloadFlatDirectoryHandler extends AbstractTerminalHandler<Sf
|
||||
public void handle(WebSocketSession channel, SftpDownloadFlatDirectoryRequest payload) {
|
||||
// 获取会话
|
||||
String sessionId = payload.getSessionId();
|
||||
ISftpSession session = terminalManager.getSession(channel.getId(), sessionId);
|
||||
ISftpSession session = hostTerminalManager.getSession(channel.getId(), sessionId);
|
||||
String[] paths = payload.getPath().split("\\|");
|
||||
log.info("SftpDownloadFlatDirectoryHandler-handle start sessionId: {}, paths: {}", sessionId, Arrays.toString(paths));
|
||||
Exception ex = null;
|
||||
|
||||
@@ -25,7 +25,7 @@ public class SftpGetContentHandler extends AbstractTerminalHandler<SftpBaseReque
|
||||
public void handle(WebSocketSession channel, SftpBaseRequest payload) {
|
||||
// 获取会话
|
||||
String sessionId = payload.getSessionId();
|
||||
ISftpSession session = terminalManager.getSession(channel.getId(), sessionId);
|
||||
ISftpSession session = hostTerminalManager.getSession(channel.getId(), sessionId);
|
||||
String path = payload.getPath();
|
||||
log.info("SftpGetContentHandler-handle start sessionId: {}, path: {}", sessionId, path);
|
||||
String content = Const.EMPTY;
|
||||
|
||||
@@ -31,7 +31,7 @@ public class SftpListHandler extends AbstractTerminalHandler<SftpListRequest> {
|
||||
public void handle(WebSocketSession channel, SftpListRequest payload) {
|
||||
// 获取会话
|
||||
String sessionId = payload.getSessionId();
|
||||
ISftpSession session = terminalManager.getSession(channel.getId(), sessionId);
|
||||
ISftpSession session = hostTerminalManager.getSession(channel.getId(), sessionId);
|
||||
String path = payload.getPath();
|
||||
log.info("SftpListHandler-handle start sessionId: {}, path: {}", sessionId, path);
|
||||
Exception ex = null;
|
||||
|
||||
@@ -30,7 +30,7 @@ public class SftpMakeDirectoryHandler extends AbstractTerminalHandler<SftpBaseRe
|
||||
long startTime = System.currentTimeMillis();
|
||||
// 获取会话
|
||||
String sessionId = payload.getSessionId();
|
||||
ISftpSession session = terminalManager.getSession(channel.getId(), sessionId);
|
||||
ISftpSession session = hostTerminalManager.getSession(channel.getId(), sessionId);
|
||||
String path = payload.getPath();
|
||||
log.info("SftpMakeDirectoryHandler-handle start sessionId: {}, path: {}", sessionId, path);
|
||||
Exception ex = null;
|
||||
|
||||
@@ -30,7 +30,7 @@ public class SftpMoveHandler extends AbstractTerminalHandler<SftpMoveRequest> {
|
||||
long startTime = System.currentTimeMillis();
|
||||
// 获取会话
|
||||
String sessionId = payload.getSessionId();
|
||||
ISftpSession session = terminalManager.getSession(channel.getId(), sessionId);
|
||||
ISftpSession session = hostTerminalManager.getSession(channel.getId(), sessionId);
|
||||
String path = payload.getPath();
|
||||
String target = payload.getTarget();
|
||||
log.info("SftpMoveHandler-handle start sessionId: {}, path: {}, target: {}", sessionId, path, target);
|
||||
|
||||
@@ -31,7 +31,7 @@ public class SftpRemoveHandler extends AbstractTerminalHandler<SftpBaseRequest>
|
||||
long startTime = System.currentTimeMillis();
|
||||
// 获取会话
|
||||
String sessionId = payload.getSessionId();
|
||||
ISftpSession session = terminalManager.getSession(channel.getId(), sessionId);
|
||||
ISftpSession session = hostTerminalManager.getSession(channel.getId(), sessionId);
|
||||
String[] paths = payload.getPath().split("\\|");
|
||||
log.info("SftpRemoveHandler-handle start sessionId: {}, path: {}", sessionId, Arrays.toString(paths));
|
||||
Exception ex = null;
|
||||
|
||||
@@ -30,7 +30,7 @@ public class SftpSetContentHandler extends AbstractTerminalHandler<SftpSetConten
|
||||
long startTime = System.currentTimeMillis();
|
||||
// 获取会话
|
||||
String sessionId = payload.getSessionId();
|
||||
ISftpSession session = terminalManager.getSession(channel.getId(), sessionId);
|
||||
ISftpSession session = hostTerminalManager.getSession(channel.getId(), sessionId);
|
||||
String path = payload.getPath();
|
||||
log.info("SftpSetContentHandler-handle start sessionId: {}, path: {}", sessionId, path);
|
||||
Exception ex = null;
|
||||
|
||||
@@ -30,7 +30,7 @@ public class SftpTouchHandler extends AbstractTerminalHandler<SftpBaseRequest> {
|
||||
long startTime = System.currentTimeMillis();
|
||||
// 获取会话
|
||||
String sessionId = payload.getSessionId();
|
||||
ISftpSession session = terminalManager.getSession(channel.getId(), sessionId);
|
||||
ISftpSession session = hostTerminalManager.getSession(channel.getId(), sessionId);
|
||||
String path = payload.getPath();
|
||||
log.info("SftpTouchHandler-handle start sessionId: {}, path: {}", sessionId, path);
|
||||
Exception ex = null;
|
||||
|
||||
@@ -30,7 +30,7 @@ public class SftpTruncateHandler extends AbstractTerminalHandler<SftpBaseRequest
|
||||
long startTime = System.currentTimeMillis();
|
||||
// 获取会话
|
||||
String sessionId = payload.getSessionId();
|
||||
ISftpSession session = terminalManager.getSession(channel.getId(), sessionId);
|
||||
ISftpSession session = hostTerminalManager.getSession(channel.getId(), sessionId);
|
||||
String path = payload.getPath();
|
||||
log.info("SftpTruncateHandler-handle start sessionId: {}, path: {}", sessionId, path);
|
||||
Exception ex = null;
|
||||
|
||||
@@ -20,7 +20,7 @@ public class SshInputHandler extends AbstractTerminalHandler<SshInputRequest> {
|
||||
@Override
|
||||
public void handle(WebSocketSession channel, SshInputRequest payload) {
|
||||
// 获取会话
|
||||
ISshSession session = terminalManager.getSession(channel.getId(), payload.getSessionId());
|
||||
ISshSession session = hostTerminalManager.getSession(channel.getId(), payload.getSessionId());
|
||||
// 处理输入
|
||||
session.write(payload.getCommand());
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ public class SshResizeHandler extends AbstractTerminalHandler<SshResizeRequest>
|
||||
@Override
|
||||
public void handle(WebSocketSession channel, SshResizeRequest payload) {
|
||||
// 获取会话
|
||||
ISshSession session = terminalManager.getSession(channel.getId(), payload.getSessionId());
|
||||
ISshSession session = hostTerminalManager.getSession(channel.getId(), payload.getSessionId());
|
||||
// 修改大小
|
||||
session.resize(payload.getCols(), payload.getRows());
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@ public class TerminalCheckHandler extends AbstractTerminalHandler<TerminalCheckR
|
||||
* @return 是否存在
|
||||
*/
|
||||
private boolean checkSession(WebSocketSession channel, TerminalCheckRequest payload) {
|
||||
ITerminalSession session = terminalManager.getSession(channel.getId(), payload.getSessionId());
|
||||
ITerminalSession session = hostTerminalManager.getSession(channel.getId(), payload.getSessionId());
|
||||
if (session != null) {
|
||||
this.sendCheckFailedMessage(channel, payload, ErrorMessage.SESSION_PRESENT);
|
||||
return true;
|
||||
|
||||
@@ -20,7 +20,7 @@ public class TerminalCloseHandler extends AbstractTerminalHandler<TerminalBasePa
|
||||
public void handle(WebSocketSession channel, TerminalBasePayload payload) {
|
||||
log.info("TerminalCloseHandler-handle start sessionId: {}", payload.getSessionId());
|
||||
// 关闭会话
|
||||
terminalManager.closeSession(channel.getId(), payload.getSessionId());
|
||||
hostTerminalManager.closeSession(channel.getId(), payload.getSessionId());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ public class TerminalConnectHandler extends AbstractTerminalHandler<TerminalConn
|
||||
// 连接主机
|
||||
ITerminalSession session = this.connect(sessionId, connect, channel, payload);
|
||||
// 添加会话到 manager
|
||||
terminalManager.addSession(session);
|
||||
hostTerminalManager.addSession(session);
|
||||
} catch (Exception e) {
|
||||
ex = e;
|
||||
// 修改连接状态为失败
|
||||
|
||||
@@ -26,7 +26,7 @@ public class TerminalPingHandler extends AbstractTerminalHandler<TerminalBasePay
|
||||
// 发送 pong
|
||||
this.send(channel, OutputTypeEnum.PONG.getType());
|
||||
// 活跃 terminal
|
||||
Map<String, ITerminalSession> sessions = terminalManager.getSession(channel.getId());
|
||||
Map<String, ITerminalSession> sessions = hostTerminalManager.getSession(channel.getId());
|
||||
if (!Maps.isEmpty(sessions)) {
|
||||
for (ITerminalSession session : sessions.values()) {
|
||||
session.keepAlive();
|
||||
|
||||
@@ -10,14 +10,14 @@ import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 终端管理器
|
||||
* 主机终端管理器
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/1/3 11:35
|
||||
*/
|
||||
@Component
|
||||
public class TerminalManager {
|
||||
public class HostTerminalManager {
|
||||
|
||||
/**
|
||||
* 会话存储器
|
||||
@@ -4,6 +4,7 @@ import com.alibaba.fastjson.JSON;
|
||||
import com.orion.lang.utils.io.Streams;
|
||||
import com.orion.visor.module.asset.handler.host.transfer.handler.ITransferHandler;
|
||||
import com.orion.visor.module.asset.handler.host.transfer.handler.TransferHandler;
|
||||
import com.orion.visor.module.asset.handler.host.transfer.manager.HostTransferManager;
|
||||
import com.orion.visor.module.asset.handler.host.transfer.model.TransferOperatorRequest;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
@@ -13,7 +14,7 @@ import org.springframework.web.socket.TextMessage;
|
||||
import org.springframework.web.socket.WebSocketSession;
|
||||
import org.springframework.web.socket.handler.AbstractWebSocketHandler;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* sftp 传输消息处理器
|
||||
@@ -26,24 +27,30 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
@Component
|
||||
public class TransferMessageDispatcher extends AbstractWebSocketHandler {
|
||||
|
||||
private final ConcurrentHashMap<String, ITransferHandler> handlers = new ConcurrentHashMap<>();
|
||||
@Resource
|
||||
private HostTransferManager hostTransferManager;
|
||||
|
||||
@Override
|
||||
public void afterConnectionEstablished(WebSocketSession session) {
|
||||
log.info("TransferMessageHandler-afterConnectionEstablished id: {}", session.getId());
|
||||
// 添加处理器
|
||||
hostTransferManager.putHandler(session.getId(), new TransferHandler(session));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleTextMessage(WebSocketSession session, TextMessage message) {
|
||||
// 获取处理器
|
||||
ITransferHandler handler = handlers.computeIfAbsent(session.getId(), s -> new TransferHandler(session));
|
||||
ITransferHandler handler = hostTransferManager.getHandler(session.getId());
|
||||
// 处理消息
|
||||
handler.handleMessage(JSON.parseObject(message.getPayload(), TransferOperatorRequest.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) {
|
||||
handlers.get(session.getId()).putContent(message.getPayload().array());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterConnectionEstablished(WebSocketSession session) {
|
||||
log.info("TransferMessageHandler-afterConnectionEstablished id: {}", session.getId());
|
||||
// 获取处理器
|
||||
ITransferHandler handler = hostTransferManager.getHandler(session.getId());
|
||||
// 添加数据
|
||||
handler.putContent(message.getPayload().array());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -56,7 +63,7 @@ public class TransferMessageDispatcher extends AbstractWebSocketHandler {
|
||||
String id = session.getId();
|
||||
log.info("TransferMessageHandler-afterConnectionClosed id: {}, code: {}, reason: {}", id, status.getCode(), status.getReason());
|
||||
// 关闭会话
|
||||
Streams.close(handlers.remove(id));
|
||||
Streams.close(hostTransferManager.removeHandler(id));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -30,9 +30,9 @@ public enum TransferOperatorType {
|
||||
UPLOAD_ERROR(TransferOperatorType.UPLOAD, "uploadError"),
|
||||
|
||||
/**
|
||||
* 开始下载
|
||||
* 初始化下载
|
||||
*/
|
||||
DOWNLOAD_START(TransferOperatorType.DOWNLOAD, "downloadStart"),
|
||||
DOWNLOAD_INIT(TransferOperatorType.DOWNLOAD, "downloadInit"),
|
||||
|
||||
/**
|
||||
* 中断下载
|
||||
@@ -45,7 +45,7 @@ public enum TransferOperatorType {
|
||||
|
||||
public static final String DOWNLOAD = "DOWNLOAD";
|
||||
|
||||
private final String operator;
|
||||
private final String kind;
|
||||
|
||||
private final String type;
|
||||
|
||||
|
||||
@@ -34,6 +34,16 @@ public enum TransferReceiverType {
|
||||
*/
|
||||
UPLOAD_ERROR("uploadError"),
|
||||
|
||||
/**
|
||||
* 开始下载
|
||||
*/
|
||||
DOWNLOAD_START("downloadStart"),
|
||||
|
||||
/**
|
||||
* 下载进度
|
||||
*/
|
||||
DOWNLOAD_PROGRESS("downloadProgress"),
|
||||
|
||||
/**
|
||||
* 下载完成
|
||||
*/
|
||||
|
||||
@@ -2,6 +2,9 @@ package com.orion.visor.module.asset.handler.host.transfer.handler;
|
||||
|
||||
import com.orion.lang.able.SafeCloseable;
|
||||
import com.orion.visor.module.asset.handler.host.transfer.model.TransferOperatorRequest;
|
||||
import com.orion.visor.module.asset.handler.host.transfer.session.IDownloadSession;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 传输处理器定义
|
||||
@@ -26,4 +29,11 @@ public interface ITransferHandler extends SafeCloseable {
|
||||
*/
|
||||
void putContent(byte[] content);
|
||||
|
||||
/**
|
||||
* 获取 token sessions
|
||||
*
|
||||
* @return token sessions
|
||||
*/
|
||||
Map<String, IDownloadSession> getTokenSessions();
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.orion.visor.module.asset.handler.host.transfer.handler;
|
||||
|
||||
import com.orion.lang.id.UUIds;
|
||||
import com.orion.lang.utils.Exceptions;
|
||||
import com.orion.lang.utils.io.Streams;
|
||||
import com.orion.net.host.SessionStore;
|
||||
@@ -14,6 +15,7 @@ import com.orion.visor.module.asset.handler.host.transfer.model.TransferOperator
|
||||
import com.orion.visor.module.asset.handler.host.transfer.session.*;
|
||||
import com.orion.visor.module.asset.handler.host.transfer.utils.TransferUtils;
|
||||
import com.orion.visor.module.asset.service.HostTerminalService;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.socket.WebSocketSession;
|
||||
|
||||
@@ -45,10 +47,14 @@ public class TransferHandler implements ITransferHandler {
|
||||
*/
|
||||
private final ConcurrentHashMap<String, ITransferHostSession> sessions;
|
||||
|
||||
@Getter
|
||||
private final ConcurrentHashMap<String, IDownloadSession> tokenSessions;
|
||||
|
||||
public TransferHandler(WebSocketSession channel) {
|
||||
this.channel = channel;
|
||||
this.userId = WebSockets.getAttr(channel, ExtraFieldConst.USER_ID);
|
||||
this.sessions = new ConcurrentHashMap<>();
|
||||
this.tokenSessions = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -73,9 +79,11 @@ public class TransferHandler implements ITransferHandler {
|
||||
// 上传失败
|
||||
((IUploadSession) currentSession).uploadError();
|
||||
break;
|
||||
case DOWNLOAD_START:
|
||||
case DOWNLOAD_INIT:
|
||||
// 开始下载
|
||||
((IDownloadSession) currentSession).startDownload(payload.getPath());
|
||||
String token = UUIds.random32();
|
||||
tokenSessions.put(token, (IDownloadSession) currentSession);
|
||||
((IDownloadSession) currentSession).downloadInit(payload.getPath(), token);
|
||||
break;
|
||||
case DOWNLOAD_ABORT:
|
||||
// 中断下载
|
||||
@@ -100,7 +108,7 @@ public class TransferHandler implements ITransferHandler {
|
||||
*/
|
||||
private boolean getAndInitSession(TransferOperatorRequest payload, TransferOperatorType type) {
|
||||
Long hostId = payload.getHostId();
|
||||
String sessionKey = hostId + "_" + type.getOperator();
|
||||
String sessionKey = hostId + "_" + type.getKind();
|
||||
try {
|
||||
// 获取会话
|
||||
ITransferHostSession session = sessions.get(sessionKey);
|
||||
@@ -109,10 +117,10 @@ public class TransferHandler implements ITransferHandler {
|
||||
HostTerminalConnectDTO connectInfo = hostTerminalService.getTerminalConnectInfo(this.userId, hostId);
|
||||
SessionStore sessionStore = hostTerminalService.openSessionStore(connectInfo);
|
||||
// 打开会话并初始化
|
||||
if (TransferOperatorType.UPLOAD.equals(type.getOperator())) {
|
||||
if (TransferOperatorType.UPLOAD.equals(type.getKind())) {
|
||||
// 上传操作
|
||||
session = new UploadSession(connectInfo, sessionStore, this.channel);
|
||||
} else if (TransferOperatorType.DOWNLOAD.equals(type.getOperator())) {
|
||||
} else if (TransferOperatorType.DOWNLOAD.equals(type.getKind())) {
|
||||
// 下载操作
|
||||
session = new DownloadSession(connectInfo, sessionStore, this.channel);
|
||||
} else {
|
||||
@@ -136,6 +144,7 @@ public class TransferHandler implements ITransferHandler {
|
||||
public void close() {
|
||||
log.info("TransferHandler.close channelId: {}", channel.getId());
|
||||
sessions.values().forEach(Streams::close);
|
||||
tokenSessions.clear();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.orion.visor.module.asset.handler.host.transfer.manager;
|
||||
|
||||
import com.orion.visor.module.asset.handler.host.transfer.handler.ITransferHandler;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 主机传输管理器
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/6/4 17:58
|
||||
*/
|
||||
@Component
|
||||
public class HostTransferManager {
|
||||
|
||||
private final ConcurrentHashMap<String, ITransferHandler> handlers = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 添加处理器
|
||||
*
|
||||
* @param id id
|
||||
* @param handler handler
|
||||
*/
|
||||
public void putHandler(String id, ITransferHandler handler) {
|
||||
handlers.put(id, handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取处理器
|
||||
*
|
||||
* @param id id
|
||||
* @return handler
|
||||
*/
|
||||
public ITransferHandler getHandler(String id) {
|
||||
return handlers.get(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除处理器
|
||||
*
|
||||
* @param id id
|
||||
* @return handler
|
||||
*/
|
||||
public ITransferHandler removeHandler(String id) {
|
||||
return handlers.remove(id);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,7 +20,7 @@ import lombok.NoArgsConstructor;
|
||||
@Schema(name = "FileOperatorRequest", description = "文件操作请求 实体对象")
|
||||
public class TransferOperatorRequest {
|
||||
|
||||
@Schema(description = "上传路径")
|
||||
@Schema(description = "文件路径")
|
||||
private String path;
|
||||
|
||||
@Schema(description = "type")
|
||||
|
||||
@@ -20,6 +20,9 @@ import lombok.NoArgsConstructor;
|
||||
@Schema(name = "FileOperatorResponse", description = "文件操作响应 实体对象")
|
||||
public class TransferOperatorResponse {
|
||||
|
||||
@Schema(description = "channelId")
|
||||
private String channelId;
|
||||
|
||||
@Schema(description = "type")
|
||||
private String type;
|
||||
|
||||
@@ -29,6 +32,12 @@ public class TransferOperatorResponse {
|
||||
@Schema(description = "是否成功")
|
||||
private Boolean success;
|
||||
|
||||
@Schema(description = "传输的大小")
|
||||
private Integer currentSize;
|
||||
|
||||
@Schema(description = "transferToken")
|
||||
private String transferToken;
|
||||
|
||||
@Schema(description = "消息")
|
||||
private String msg;
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.orion.visor.module.asset.handler.host.transfer.session;
|
||||
|
||||
import com.orion.lang.define.wrapper.Ref;
|
||||
import com.orion.lang.utils.Threads;
|
||||
import com.orion.lang.utils.Valid;
|
||||
import com.orion.lang.utils.io.Streams;
|
||||
@@ -12,11 +13,13 @@ import com.orion.visor.module.asset.define.operator.HostTerminalOperatorType;
|
||||
import com.orion.visor.module.asset.entity.dto.HostTerminalConnectDTO;
|
||||
import com.orion.visor.module.asset.handler.host.transfer.enums.TransferReceiverType;
|
||||
import com.orion.visor.module.asset.handler.host.transfer.utils.TransferUtils;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.socket.BinaryMessage;
|
||||
import org.springframework.web.socket.WebSocketSession;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* 下载会话实现
|
||||
@@ -28,6 +31,9 @@ import java.io.InputStream;
|
||||
@Slf4j
|
||||
public class DownloadSession extends TransferHostSession implements IDownloadSession {
|
||||
|
||||
@Getter
|
||||
private String path;
|
||||
|
||||
private InputStream inputStream;
|
||||
|
||||
public DownloadSession(HostTerminalConnectDTO connectInfo, SessionStore sessionStore, WebSocketSession channel) {
|
||||
@@ -35,7 +41,8 @@ public class DownloadSession extends TransferHostSession implements IDownloadSes
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startDownload(String path) {
|
||||
public void downloadInit(String path, String token) {
|
||||
this.path = path;
|
||||
String channelId = channel.getId();
|
||||
try {
|
||||
log.info("DownloadSession.startDownload open start channelId: {}, path: {}", channelId, path);
|
||||
@@ -54,39 +61,17 @@ public class DownloadSession extends TransferHostSession implements IDownloadSes
|
||||
}
|
||||
// 打开输入流
|
||||
this.inputStream = executor.openInputStream(path);
|
||||
// 响应开始下载
|
||||
TransferUtils.sendMessage(this.channel, TransferReceiverType.DOWNLOAD_START, null, e -> {
|
||||
e.setChannelId(channelId);
|
||||
e.setTransferToken(token);
|
||||
});
|
||||
log.info("DownloadSession.startDownload open success channelId: {}, path: {}", channelId, path);
|
||||
} catch (Exception e) {
|
||||
log.error("DownloadSession.startDownload open error channelId: {}, path: {}", channelId, path, e);
|
||||
// 响应结果
|
||||
// 响应下载失败
|
||||
TransferUtils.sendMessage(this.channel, TransferReceiverType.DOWNLOAD_ERROR, e);
|
||||
return;
|
||||
}
|
||||
// 异步读取文件内容
|
||||
AssetThreadPools.TERMINAL_OPERATOR.execute(() -> {
|
||||
Exception ex = null;
|
||||
try {
|
||||
byte[] buffer = new byte[Const.BUFFER_KB_32];
|
||||
int len;
|
||||
// 响应文件内容
|
||||
while (this.inputStream != null && (len = this.inputStream.read(buffer)) != -1) {
|
||||
this.channel.sendMessage(new BinaryMessage(buffer, 0, len, true));
|
||||
}
|
||||
log.info("DownloadSession.download finish channelId: {}, path: {}", channelId, path);
|
||||
} catch (Exception e) {
|
||||
log.error("DownloadSession.download error channelId: {}, path: {}", channelId, path, e);
|
||||
ex = e;
|
||||
}
|
||||
// 关闭等待 jsch 内部处理
|
||||
Threads.sleep(100);
|
||||
this.closeStream();
|
||||
Threads.sleep(100);
|
||||
// 响应结果
|
||||
if (ex == null) {
|
||||
TransferUtils.sendMessage(this.channel, TransferReceiverType.DOWNLOAD_FINISH, null);
|
||||
} else {
|
||||
TransferUtils.sendMessage(this.channel, TransferReceiverType.DOWNLOAD_ERROR, ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -96,9 +81,72 @@ public class DownloadSession extends TransferHostSession implements IDownloadSes
|
||||
this.closeStream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(OutputStream outputStream) {
|
||||
String channelId = channel.getId();
|
||||
Ref<Exception> ex = new Ref<>();
|
||||
try {
|
||||
byte[] buffer = new byte[Const.BUFFER_KB_32];
|
||||
int len;
|
||||
int i = 0;
|
||||
int size = 0;
|
||||
// 响应文件内容
|
||||
while (this.inputStream != null && (len = this.inputStream.read(buffer)) != -1) {
|
||||
outputStream.write(buffer, 0, len);
|
||||
size += len;
|
||||
// 不要每次都 flush 和 send > 1mb
|
||||
if (i == 32) {
|
||||
i = 0;
|
||||
}
|
||||
// 首次触发
|
||||
if (i == 0) {
|
||||
this.flushAndSendProgress(outputStream, size);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
// 最后一次也要 flush
|
||||
if (i != 0) {
|
||||
this.flushAndSendProgress(outputStream, size);
|
||||
}
|
||||
log.info("DownloadSession.download finish channelId: {}, path: {}", channelId, path);
|
||||
} catch (Exception e) {
|
||||
log.error("DownloadSession.download error channelId: {}, path: {}", channelId, path, e);
|
||||
ex.set(e);
|
||||
}
|
||||
// 异步关闭
|
||||
AssetThreadPools.TERMINAL_OPERATOR.execute(() -> {
|
||||
// 关闭等待 jsch 内部处理
|
||||
Threads.sleep(100);
|
||||
this.closeStream();
|
||||
Threads.sleep(100);
|
||||
// 响应结果
|
||||
Exception e = ex.getValue();
|
||||
if (e == null) {
|
||||
TransferUtils.sendMessage(this.channel, TransferReceiverType.DOWNLOAD_FINISH, null);
|
||||
} else {
|
||||
TransferUtils.sendMessage(this.channel, TransferReceiverType.DOWNLOAD_ERROR, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷流 & 发送进度
|
||||
*
|
||||
* @param outputStream outputStream
|
||||
* @param size size
|
||||
* @throws IOException IOException
|
||||
*/
|
||||
private void flushAndSendProgress(OutputStream outputStream, int size) throws IOException {
|
||||
// flush
|
||||
outputStream.flush();
|
||||
// send
|
||||
TransferUtils.sendMessage(this.channel, TransferReceiverType.DOWNLOAD_PROGRESS, null, e -> e.setCurrentSize(size));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void closeStream() {
|
||||
// 关闭 inputStream 可能会被阻塞 ???...??? 只能关闭 executor
|
||||
this.path = null;
|
||||
Streams.close(this.executor);
|
||||
this.executor = null;
|
||||
this.inputStream = null;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.orion.visor.module.asset.handler.host.transfer.session;
|
||||
|
||||
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
|
||||
|
||||
/**
|
||||
* 下载会话定义
|
||||
*
|
||||
@@ -7,18 +9,26 @@ package com.orion.visor.module.asset.handler.host.transfer.session;
|
||||
* @version 1.0.0
|
||||
* @since 2024/2/22 22:25
|
||||
*/
|
||||
public interface IDownloadSession {
|
||||
public interface IDownloadSession extends StreamingResponseBody {
|
||||
|
||||
/**
|
||||
* 开始下载
|
||||
* 初始化下载
|
||||
*
|
||||
* @param path path
|
||||
* @param path path
|
||||
* @param token token
|
||||
*/
|
||||
void startDownload(String path);
|
||||
void downloadInit(String path, String token);
|
||||
|
||||
/**
|
||||
* 停止下载
|
||||
*/
|
||||
void abortDownload();
|
||||
|
||||
/**
|
||||
* 获取下载文件路径
|
||||
*
|
||||
* @return path
|
||||
*/
|
||||
String getPath();
|
||||
|
||||
}
|
||||
|
||||
@@ -6,8 +6,11 @@ import com.orion.visor.framework.common.constant.ErrorMessage;
|
||||
import com.orion.visor.framework.websocket.core.utils.WebSockets;
|
||||
import com.orion.visor.module.asset.handler.host.transfer.enums.TransferReceiverType;
|
||||
import com.orion.visor.module.asset.handler.host.transfer.model.TransferOperatorResponse;
|
||||
import org.apache.catalina.connector.ClientAbortException;
|
||||
import org.springframework.web.socket.WebSocketSession;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* 传输工具类
|
||||
*
|
||||
@@ -28,11 +31,26 @@ public class TransferUtils {
|
||||
* @param ex ex
|
||||
*/
|
||||
public static void sendMessage(WebSocketSession channel, TransferReceiverType type, Exception ex) {
|
||||
sendMessage(channel, type, ex, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送消息
|
||||
*
|
||||
* @param channel channel
|
||||
* @param type type
|
||||
* @param ex ex
|
||||
* @param filler filler
|
||||
*/
|
||||
public static void sendMessage(WebSocketSession channel, TransferReceiverType type, Exception ex, Consumer<TransferOperatorResponse> filler) {
|
||||
TransferOperatorResponse resp = TransferOperatorResponse.builder()
|
||||
.type(type.getType())
|
||||
.success(ex == null)
|
||||
.msg(TransferUtils.getErrorMessage(ex))
|
||||
.build();
|
||||
if (filler != null) {
|
||||
filler.accept(resp);
|
||||
}
|
||||
WebSockets.sendText(channel, JSON.toJSONString(resp));
|
||||
}
|
||||
|
||||
@@ -45,9 +63,10 @@ public class TransferUtils {
|
||||
public static String getErrorMessage(Exception ex) {
|
||||
if (ex == null) {
|
||||
return null;
|
||||
}
|
||||
if (ex instanceof InvalidArgumentException) {
|
||||
} else if (ex instanceof InvalidArgumentException) {
|
||||
return ex.getMessage();
|
||||
} else if (ex instanceof ClientAbortException) {
|
||||
return ErrorMessage.CLIENT_ABORT;
|
||||
}
|
||||
return ErrorMessage.OPERATE_ERROR;
|
||||
}
|
||||
|
||||
@@ -3,17 +3,19 @@ package com.orion.visor.module.asset.service;
|
||||
import com.orion.lang.define.wrapper.DataGrid;
|
||||
import com.orion.visor.module.asset.entity.request.host.HostSftpLogQueryRequest;
|
||||
import com.orion.visor.module.asset.entity.vo.HostSftpLogVO;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* SFTP 操作日志 服务类
|
||||
* SFTP 操作 服务类
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023-12-26 22:09
|
||||
*/
|
||||
public interface HostSftpLogService {
|
||||
public interface HostSftpService {
|
||||
|
||||
/**
|
||||
* 分页查询 SFTP 操作日志
|
||||
@@ -31,4 +33,16 @@ public interface HostSftpLogService {
|
||||
*/
|
||||
Integer deleteHostSftpLog(List<Long> idList);
|
||||
|
||||
/**
|
||||
* 通过 transferToken 下载
|
||||
*
|
||||
* @param channelId channelId
|
||||
* @param transferToken transferToken
|
||||
* @param response response
|
||||
* @return body
|
||||
*/
|
||||
StreamingResponseBody downloadWithTransferToken(String channelId,
|
||||
String transferToken,
|
||||
HttpServletResponse response);
|
||||
|
||||
}
|
||||
@@ -19,7 +19,7 @@ import com.orion.visor.module.asset.entity.request.host.HostConnectLogQueryReque
|
||||
import com.orion.visor.module.asset.entity.vo.HostConnectLogVO;
|
||||
import com.orion.visor.module.asset.enums.HostConnectStatusEnum;
|
||||
import com.orion.visor.module.asset.enums.HostConnectTypeEnum;
|
||||
import com.orion.visor.module.asset.handler.host.terminal.manager.TerminalManager;
|
||||
import com.orion.visor.module.asset.handler.host.terminal.manager.HostTerminalManager;
|
||||
import com.orion.visor.module.asset.handler.host.terminal.model.TerminalConfig;
|
||||
import com.orion.visor.module.asset.handler.host.terminal.session.ITerminalSession;
|
||||
import com.orion.visor.module.asset.service.HostConnectLogService;
|
||||
@@ -49,7 +49,7 @@ public class HostConnectLogServiceImpl implements HostConnectLogService {
|
||||
private HostConnectLogDAO hostConnectLogDAO;
|
||||
|
||||
@Resource
|
||||
private TerminalManager terminalManager;
|
||||
private HostTerminalManager hostTerminalManager;
|
||||
|
||||
@Override
|
||||
public Long create(HostConnectTypeEnum type, HostConnectLogCreateRequest request) {
|
||||
@@ -84,7 +84,7 @@ public class HostConnectLogServiceImpl implements HostConnectLogService {
|
||||
@Override
|
||||
public List<HostConnectLogVO> getHostConnectSessions(HostConnectLogQueryRequest request) {
|
||||
// 查询全部
|
||||
List<Long> idList = terminalManager.getChannelSessions()
|
||||
List<Long> idList = hostTerminalManager.getChannelSessions()
|
||||
.values()
|
||||
.stream()
|
||||
.map(ConcurrentHashMap::values)
|
||||
@@ -204,7 +204,7 @@ public class HostConnectLogServiceImpl implements HostConnectLogService {
|
||||
OperatorLogs.add(OperatorLogs.HOST_NAME, record.getHostName());
|
||||
// 获取会话
|
||||
HostConnectLogExtraDTO extra = JSON.parseObject(record.getExtraInfo(), HostConnectLogExtraDTO.class);
|
||||
ITerminalSession session = terminalManager.getSession(extra.getChannelId(), extra.getSessionId());
|
||||
ITerminalSession session = hostTerminalManager.getSession(extra.getChannelId(), extra.getSessionId());
|
||||
if (session != null) {
|
||||
// 关闭会话
|
||||
session.forceOffline();
|
||||
|
||||
@@ -2,26 +2,37 @@ package com.orion.visor.module.asset.service.impl;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.orion.lang.constant.StandardContentType;
|
||||
import com.orion.lang.define.wrapper.DataGrid;
|
||||
import com.orion.lang.utils.Arrays1;
|
||||
import com.orion.lang.utils.Strings;
|
||||
import com.orion.lang.utils.io.Files1;
|
||||
import com.orion.visor.framework.biz.operator.log.core.utils.OperatorLogs;
|
||||
import com.orion.visor.framework.common.constant.Const;
|
||||
import com.orion.visor.framework.common.constant.ErrorMessage;
|
||||
import com.orion.visor.framework.common.constant.ExtraFieldConst;
|
||||
import com.orion.visor.module.asset.convert.HostSftpLogConvert;
|
||||
import com.orion.visor.module.asset.define.operator.HostTerminalOperatorType;
|
||||
import com.orion.visor.module.asset.entity.request.host.HostSftpLogQueryRequest;
|
||||
import com.orion.visor.module.asset.entity.vo.HostSftpLogVO;
|
||||
import com.orion.visor.module.asset.service.HostSftpLogService;
|
||||
import com.orion.visor.module.asset.handler.host.transfer.handler.ITransferHandler;
|
||||
import com.orion.visor.module.asset.handler.host.transfer.manager.HostTransferManager;
|
||||
import com.orion.visor.module.asset.handler.host.transfer.session.IDownloadSession;
|
||||
import com.orion.visor.module.asset.service.HostSftpService;
|
||||
import com.orion.visor.module.infra.api.OperatorLogApi;
|
||||
import com.orion.visor.module.infra.entity.dto.operator.OperatorLogQueryDTO;
|
||||
import com.orion.web.servlet.web.Servlets;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* SFTP 操作日志 服务实现类
|
||||
* SFTP 操作 服务实现类
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
@@ -29,11 +40,14 @@ import java.util.List;
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class HostSftpLogServiceImpl implements HostSftpLogService {
|
||||
public class HostSftpServiceImpl implements HostSftpService {
|
||||
|
||||
@Resource
|
||||
private OperatorLogApi operatorLogApi;
|
||||
|
||||
@Resource
|
||||
private HostTransferManager hostTransferManager;
|
||||
|
||||
@Override
|
||||
public DataGrid<HostSftpLogVO> getHostSftpLogPage(HostSftpLogQueryRequest request) {
|
||||
// 查询
|
||||
@@ -62,6 +76,25 @@ public class HostSftpLogServiceImpl implements HostSftpLogService {
|
||||
return effect;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StreamingResponseBody downloadWithTransferToken(String channelId, String transferToken, HttpServletResponse response) {
|
||||
// 获取会话
|
||||
IDownloadSession session = Optional.ofNullable(channelId)
|
||||
.map(hostTransferManager::getHandler)
|
||||
.map(ITransferHandler::getTokenSessions)
|
||||
.map(s -> s.remove(transferToken))
|
||||
.orElse(null);
|
||||
// 响应会话
|
||||
if (session == null) {
|
||||
Servlets.setContentType(response, StandardContentType.TEXT_HTML);
|
||||
Servlets.setCharset(response, Const.UTF_8);
|
||||
return outputStream -> outputStream.write(Strings.bytes(ErrorMessage.SESSION_ABSENT));
|
||||
}
|
||||
// 响应文件
|
||||
Servlets.setAttachmentHeader(response, Files1.getFileName(session.getPath()));
|
||||
return session;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建查询对象
|
||||
*
|
||||
@@ -24,6 +24,7 @@ spring:
|
||||
redisson:
|
||||
threads: 2
|
||||
netty-threads: 2
|
||||
minimum-idle-size: 2
|
||||
|
||||
mybatis-plus:
|
||||
lazy-initialization: true
|
||||
|
||||
@@ -65,6 +65,9 @@ public class TerminalPreferenceModel implements PreferenceModel {
|
||||
@Schema(description = "行高")
|
||||
private Double lineHeight;
|
||||
|
||||
@Schema(description = "字间距")
|
||||
private Integer letterSpacing;
|
||||
|
||||
@Schema(description = "文本字重")
|
||||
private String fontWeight;
|
||||
|
||||
@@ -129,6 +132,9 @@ public class TerminalPreferenceModel implements PreferenceModel {
|
||||
@Schema(description = "WebGL 渲染插件")
|
||||
private Boolean enableWebglPlugin;
|
||||
|
||||
@Schema(description = "unicode11 插件")
|
||||
private Boolean enableUnicodePlugin;
|
||||
|
||||
@Schema(description = "图片渲染插件")
|
||||
private Boolean enableImagePlugin;
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ public class TerminalPreferenceStrategy implements IPreferenceStrategy<TerminalP
|
||||
.fontFamily("_")
|
||||
.fontSize(13)
|
||||
.lineHeight(1.12)
|
||||
.letterSpacing(1)
|
||||
.fontWeight("normal")
|
||||
.fontWeightBold("bold")
|
||||
.cursorStyle("bar")
|
||||
@@ -48,6 +49,7 @@ public class TerminalPreferenceStrategy implements IPreferenceStrategy<TerminalP
|
||||
String defaultPluginsSetting = TerminalPreferenceModel.PluginsSettingModel.builder()
|
||||
.enableWeblinkPlugin(true)
|
||||
.enableWebglPlugin(true)
|
||||
.enableUnicodePlugin(true)
|
||||
.enableImagePlugin(false)
|
||||
.build()
|
||||
.toJsonString();
|
||||
|
||||
@@ -24,6 +24,7 @@ spring:
|
||||
redisson:
|
||||
threads: 2
|
||||
netty-threads: 2
|
||||
minimum-idle-size: 2
|
||||
|
||||
mybatis-plus:
|
||||
lazy-initialization: true
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
VITE_API_BASE_URL= 'http://127.0.0.1:9200/orion-visor/api'
|
||||
VITE_WS_BASE_URL= 'ws://127.0.0.1:9200/orion-visor/keep-alive'
|
||||
VITE_APP_VERSION= '2.0.4'
|
||||
VITE_APP_VERSION= '2.0.5'
|
||||
VITE_SFTP_PREVIEW_MB= 2
|
||||
VITE_DEMO_MODE= false
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
VITE_API_BASE_URL= '/orion-visor/api'
|
||||
VITE_WS_BASE_URL= '/orion-visor/keep-alive'
|
||||
VITE_APP_VERSION= '2.0.4'
|
||||
VITE_APP_VERSION= '2.0.5'
|
||||
VITE_SFTP_PREVIEW_MB= 2
|
||||
VITE_DEMO_MODE= false
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "orion-visor-ui",
|
||||
"description": "Orion Visor UI",
|
||||
"version": "2.0.4",
|
||||
"version": "2.0.5",
|
||||
"private": true,
|
||||
"author": "Jiahang Li",
|
||||
"license": "Apache 2.0",
|
||||
@@ -36,6 +36,14 @@
|
||||
"@dangojs/a-query-header": "^0.0.31",
|
||||
"@sanqi377/arco-vue-icon-picker": "^1.0.7",
|
||||
"@vueuse/core": "^9.3.0",
|
||||
"@xterm/addon-canvas": "^0.7.0",
|
||||
"@xterm/addon-fit": "^0.10.0",
|
||||
"@xterm/addon-image": "^0.8.0",
|
||||
"@xterm/addon-search": "^0.15.0",
|
||||
"@xterm/addon-unicode11": "^0.8.0",
|
||||
"@xterm/addon-web-links": "^0.11.0",
|
||||
"@xterm/addon-webgl": "^0.18.0",
|
||||
"@xterm/xterm": "^5.5.0",
|
||||
"axios": "^0.24.0",
|
||||
"cron-parser": "^4.9.0",
|
||||
"dayjs": "^1.11.5",
|
||||
@@ -53,14 +61,7 @@
|
||||
"vue": "^3.2.40",
|
||||
"vue-echarts": "^6.2.3",
|
||||
"vue-i18n": "^9.2.2",
|
||||
"vue-router": "^4.0.14",
|
||||
"xterm": "^5.3.0",
|
||||
"xterm-addon-canvas": "^0.5.0",
|
||||
"xterm-addon-fit": "^0.8.0",
|
||||
"xterm-addon-image": "^0.5.0",
|
||||
"xterm-addon-search": "^0.13.0",
|
||||
"xterm-addon-web-links": "^0.9.0",
|
||||
"xterm-addon-webgl": "^0.16.0"
|
||||
"vue-router": "^4.0.14"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@arco-plugins/vite-vue": "^1.4.5",
|
||||
@@ -93,7 +94,7 @@
|
||||
"rollup": "^3.9.1",
|
||||
"rollup-plugin-visualizer": "^5.8.2",
|
||||
"sass": "^1.69.4",
|
||||
"stylelint": "^14.13.0",
|
||||
"stylelint": "^14.14.0",
|
||||
"stylelint-config-prettier": "^9.0.3",
|
||||
"stylelint-config-rational-order": "^0.1.2",
|
||||
"stylelint-config-recommended-vue": "^1.4.0",
|
||||
|
||||
1742
orion-visor-ui/pnpm-lock.yaml
generated
1742
orion-visor-ui/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,6 @@
|
||||
import type { DataGrid, Pagination } from '@/types/global';
|
||||
import type { TableData } from '@arco-design/web-vue/es/table/interface';
|
||||
import { httpBaseUrl } from '@/utils/env';
|
||||
import axios from 'axios';
|
||||
import qs from 'query-string';
|
||||
|
||||
@@ -46,17 +47,25 @@ export interface HostSftpLogExtra {
|
||||
* 分页查询 SFTP 操作日志
|
||||
*/
|
||||
export function getHostSftpLogPage(request: HostSftpLogQueryRequest) {
|
||||
return axios.post<DataGrid<HostSftpLogQueryResponse>>('/asset/host-sftp-log/query', request);
|
||||
return axios.post<DataGrid<HostSftpLogQueryResponse>>('/asset/host-sftp/query-log', request);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除 SFTP 操作日志
|
||||
*/
|
||||
export function deleteHostSftpLog(idList: Array<number>) {
|
||||
return axios.delete('/asset/host-sftp-log/delete', {
|
||||
return axios.delete('/asset/host-sftp/delete-log', {
|
||||
params: { idList },
|
||||
paramsSerializer: params => {
|
||||
return qs.stringify(params, { arrayFormat: 'comma' });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载文件
|
||||
*/
|
||||
export function downloadWithTransferToken(channelId: string, transferToken: string) {
|
||||
window.open(`${httpBaseUrl}/asset/host-sftp/download?channelId=${channelId}&transferToken=${transferToken}`, 'newWindow');
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import type { IDisposable, ITerminalInitOnlyOptions, ITerminalOptions, Terminal } from 'xterm';
|
||||
import type { FitAddon } from 'xterm-addon-fit';
|
||||
import type { SearchAddon } from 'xterm-addon-search';
|
||||
import type { WebLinksAddon } from 'xterm-addon-web-links';
|
||||
import type { WebglAddon } from 'xterm-addon-webgl';
|
||||
import type { IDisposable, ITerminalInitOnlyOptions, ITerminalOptions, Terminal } from '@xterm/xterm';
|
||||
import type { FitAddon } from '@xterm/addon-fit';
|
||||
import type { SearchAddon } from '@xterm/addon-search';
|
||||
import type { WebLinksAddon } from '@xterm/addon-web-links';
|
||||
import type { WebglAddon } from '@xterm/addon-webgl';
|
||||
import type { Unicode11Addon } from '@xterm/addon-unicode11';
|
||||
|
||||
// 执行类型
|
||||
export type ExecType = 'BATCH' | 'JOB';
|
||||
@@ -61,6 +62,7 @@ export const LogAppenderOptions: ITerminalOptions & ITerminalInitOnlyOptions = {
|
||||
fontSize: 13,
|
||||
lineHeight: 1.12,
|
||||
convertEol: true,
|
||||
allowProposedApi: true,
|
||||
};
|
||||
|
||||
// dom 引用
|
||||
@@ -85,6 +87,7 @@ export interface LogAddons extends Record<string, IDisposable> {
|
||||
webgl: WebglAddon;
|
||||
search: SearchAddon;
|
||||
weblink: WebLinksAddon;
|
||||
unicode: Unicode11Addon;
|
||||
}
|
||||
|
||||
// 执行日志 appender 定义
|
||||
|
||||
@@ -8,11 +8,12 @@ import { Message } from '@arco-design/web-vue';
|
||||
import { useDebounceFn } from '@vueuse/core';
|
||||
import { addEventListen, removeEventListen } from '@/utils/event';
|
||||
import { copy as copyText } from '@/hooks/copy';
|
||||
import { Terminal } from 'xterm';
|
||||
import { FitAddon } from 'xterm-addon-fit';
|
||||
import { SearchAddon } from 'xterm-addon-search';
|
||||
import { WebLinksAddon } from 'xterm-addon-web-links';
|
||||
import { WebglAddon } from 'xterm-addon-webgl';
|
||||
import { Terminal } from '@xterm/xterm';
|
||||
import { FitAddon } from '@xterm/addon-fit';
|
||||
import { SearchAddon } from '@xterm/addon-search';
|
||||
import { WebLinksAddon } from '@xterm/addon-web-links';
|
||||
import { WebglAddon } from '@xterm/addon-webgl';
|
||||
import { Unicode11Addon } from '@xterm/addon-unicode11';
|
||||
|
||||
// 执行日志 appender 实现
|
||||
export default class LogAppender implements ILogAppender {
|
||||
@@ -130,15 +131,19 @@ export default class LogAppender implements ILogAppender {
|
||||
const search = new SearchAddon();
|
||||
const webgl = new WebglAddon();
|
||||
const weblink = new WebLinksAddon();
|
||||
const unicode = new Unicode11Addon();
|
||||
terminal.loadAddon(fit);
|
||||
terminal.loadAddon(search);
|
||||
terminal.loadAddon(webgl);
|
||||
terminal.loadAddon(weblink);
|
||||
terminal.loadAddon(unicode);
|
||||
terminal.unicode.activeVersion = '11';
|
||||
return {
|
||||
fit,
|
||||
search,
|
||||
webgl,
|
||||
weblink
|
||||
weblink,
|
||||
unicode
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -176,7 +176,7 @@
|
||||
import { downloadExecJobLogFile } from '@/api/job/exec-job-log';
|
||||
import { downloadFile } from '@/utils/file';
|
||||
import XtermSearchModal from '@/components/xtrem/search-modal/index.vue';
|
||||
import 'xterm/css/xterm.css';
|
||||
import '@xterm/xterm/css/xterm.css';
|
||||
|
||||
const props = defineProps<{
|
||||
type: ExecType;
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { ISearchOptions } from 'xterm-addon-search';
|
||||
import type { ISearchOptions } from '@xterm/addon-search';
|
||||
import useVisible from '@/hooks/visible';
|
||||
import { nextTick, ref } from 'vue';
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ export interface TerminalDisplaySetting {
|
||||
fontFamily?: string;
|
||||
fontSize?: number;
|
||||
lineHeight?: number;
|
||||
letterSpacing?: number;
|
||||
fontWeight?: string | number;
|
||||
fontWeightBold?: string | number;
|
||||
cursorStyle?: string;
|
||||
@@ -61,6 +62,7 @@ export interface TerminalInteractSetting {
|
||||
export interface TerminalPluginsSetting {
|
||||
enableWeblinkPlugin: boolean;
|
||||
enableWebglPlugin: boolean;
|
||||
enableUnicodePlugin: boolean;
|
||||
enableImagePlugin: boolean;
|
||||
}
|
||||
|
||||
|
||||
@@ -139,8 +139,9 @@
|
||||
onOk: async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
// 调用删除
|
||||
await clearHostConnectLog(formModel.value);
|
||||
// 调用清空
|
||||
const { data } = await clearHostConnectLog(formModel.value);
|
||||
Message.success(`已成功清空 ${data} 条数据`);
|
||||
emits('clear');
|
||||
// 清空
|
||||
setVisible(false);
|
||||
|
||||
@@ -172,9 +172,9 @@
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { HostSftpLogQueryRequest, HostSftpLogQueryResponse } from '@/api/asset/host-sftp-log';
|
||||
import type { HostSftpLogQueryRequest, HostSftpLogQueryResponse } from '@/api/asset/host-sftp';
|
||||
import { reactive, ref, onMounted } from 'vue';
|
||||
import { getHostSftpLogPage, deleteHostSftpLog } from '@/api/asset/host-sftp-log';
|
||||
import { getHostSftpLogPage, deleteHostSftpLog } from '@/api/asset/host-sftp';
|
||||
import { sftpOperatorTypeKey, sftpOperatorResultKey, SftpOperatorType } from '../types/const';
|
||||
import { usePagination, useRowSelection } from '@/types/table';
|
||||
import { useDictStore } from '@/store';
|
||||
|
||||
@@ -131,8 +131,9 @@
|
||||
onOk: async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
// 调用删除
|
||||
await clearExecCommandLog(formModel.value);
|
||||
// 调用清空
|
||||
const { data } = await clearExecCommandLog(formModel.value);
|
||||
Message.success(`已成功清空 ${data} 条数据`);
|
||||
emits('clear');
|
||||
// 清空
|
||||
setVisible(false);
|
||||
|
||||
@@ -131,8 +131,9 @@
|
||||
onOk: async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
// 调用删除
|
||||
await clearUploadTask(formModel.value);
|
||||
// 调用清空
|
||||
const { data } = await clearUploadTask(formModel.value);
|
||||
Message.success(`已成功清空 ${data} 条数据`);
|
||||
emits('clear');
|
||||
// 清空
|
||||
setVisible(false);
|
||||
|
||||
@@ -13,72 +13,86 @@
|
||||
<a-form class="terminal-setting-form"
|
||||
:model="formModel"
|
||||
layout="vertical">
|
||||
<a-space>
|
||||
<a-row :gutter="48">
|
||||
<!-- 字体样式 -->
|
||||
<a-form-item field="fontFamily" label="字体样式">
|
||||
<a-select v-model="formModel.fontFamily"
|
||||
class="form-item-font-family"
|
||||
placeholder="请选择字体样式"
|
||||
:options="toOptions(fontFamilyKey)"
|
||||
:allow-create="true"
|
||||
:filter-option="labelFilter">
|
||||
<template #option="{ data }">
|
||||
<span :style="{ fontFamily: data.value }">{{ data.label }}</span>
|
||||
</template>
|
||||
<template #label="{ data }">
|
||||
<span :style="{ fontFamily: data.value }">{{ data.label }}</span>
|
||||
</template>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-col :span="12">
|
||||
<a-form-item field="fontFamily" label="字体样式">
|
||||
<a-select v-model="formModel.fontFamily"
|
||||
placeholder="请选择字体样式"
|
||||
:options="toOptions(fontFamilyKey)"
|
||||
:allow-create="true"
|
||||
:filter-option="labelFilter">
|
||||
<template #option="{ data }">
|
||||
<span :style="{ fontFamily: data.value }">{{ data.label }}</span>
|
||||
</template>
|
||||
<template #label="{ data }">
|
||||
<span :style="{ fontFamily: data.value }">{{ data.label }}</span>
|
||||
</template>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<!-- 字体大小 -->
|
||||
<a-form-item field="fontSize" label="字体大小">
|
||||
<a-select v-model="formModel.fontSize"
|
||||
class="form-item-font-size"
|
||||
placeholder="请选择字体大小"
|
||||
:options="toOptions(fontSizeKey)" />
|
||||
</a-form-item>
|
||||
<a-col :span="12">
|
||||
<a-form-item field="fontSize" label="字体大小">
|
||||
<a-select v-model="formModel.fontSize"
|
||||
placeholder="请选择字体大小"
|
||||
:options="toOptions(fontSizeKey)" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<!-- 行高 -->
|
||||
<a-form-item field="lineHeight" label="行高">
|
||||
<a-input-number v-model="formModel.lineHeight"
|
||||
class="form-item-line-height"
|
||||
placeholder="请输入行高"
|
||||
:precision="2"
|
||||
:min="1"
|
||||
:max="2"
|
||||
hide-button />
|
||||
</a-form-item>
|
||||
</a-space>
|
||||
<a-space>
|
||||
<a-col :span="12">
|
||||
<a-form-item field="lineHeight" label="行高">
|
||||
<a-input-number v-model="formModel.lineHeight"
|
||||
placeholder="请输入行高"
|
||||
:precision="2"
|
||||
:step="0.05"
|
||||
:min="1"
|
||||
:max="2" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<!-- 字间距 -->
|
||||
<a-col :span="12">
|
||||
<a-form-item field="lineHeight" label="字间距 (px)">
|
||||
<a-input-number v-model="formModel.letterSpacing"
|
||||
placeholder="请输入字间距"
|
||||
:precision="0"
|
||||
:step="1"
|
||||
:min="-5"
|
||||
:max="5" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<!-- 普通文本字重 -->
|
||||
<a-form-item field="fontWeight" label="普通文本字重">
|
||||
<a-select v-model="formModel.fontWeight"
|
||||
class="form-item-font-weight"
|
||||
placeholder="请选择字重"
|
||||
:options="toOptions(fontWeightKey)" />
|
||||
</a-form-item>
|
||||
<a-col :span="12">
|
||||
<a-form-item field="fontWeight" label="普通文本字重">
|
||||
<a-select v-model="formModel.fontWeight"
|
||||
placeholder="请选择字重"
|
||||
:options="toOptions(fontWeightKey)" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<!-- 加粗文本字重 -->
|
||||
<a-form-item field="fontWeightBold" label="加粗文本字重">
|
||||
<a-select v-model="formModel.fontWeightBold"
|
||||
class="form-item-font-bold-weight"
|
||||
placeholder="请选择字重"
|
||||
:options="toOptions(fontWeightKey)" />
|
||||
</a-form-item>
|
||||
</a-space>
|
||||
<a-space>
|
||||
<a-col :span="12">
|
||||
<a-form-item field="fontWeightBold" label="加粗文本字重">
|
||||
<a-select v-model="formModel.fontWeightBold"
|
||||
placeholder="请选择字重"
|
||||
:options="toOptions(fontWeightKey)" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<!-- 光标样式 -->
|
||||
<a-form-item field="cursorStyle" label="光标样式">
|
||||
<a-radio-group type="button"
|
||||
v-model="formModel.cursorStyle"
|
||||
class="form-item-cursor-style usn"
|
||||
:options="toRadioOptions(cursorStyleKey)" />
|
||||
</a-form-item>
|
||||
<a-col :span="12">
|
||||
<a-form-item field="cursorStyle" label="光标样式">
|
||||
<a-radio-group type="button"
|
||||
v-model="formModel.cursorStyle"
|
||||
class="form-item-cursor-style usn"
|
||||
:options="toRadioOptions(cursorStyleKey)" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<!-- 光标闪烁 -->
|
||||
<a-form-item field="cursorBlink" label="光标是否闪烁">
|
||||
<a-switch v-model="formModel.cursorBlink"
|
||||
type="round"
|
||||
class="form-item-cursor-blink" />
|
||||
</a-form-item>
|
||||
</a-space>
|
||||
<a-col :span="12">
|
||||
<a-form-item field="cursorBlink" label="光标是否闪烁">
|
||||
<a-switch v-model="formModel.cursorBlink" type="round" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form>
|
||||
<!-- 预览区域 -->
|
||||
<div class="terminal-example">
|
||||
@@ -101,7 +115,7 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { TerminalDisplaySetting } from '@/store/modules/terminal/types';
|
||||
import { ref, watch } from 'vue';
|
||||
import { ref, watch, onMounted } from 'vue';
|
||||
import { useDictStore, useTerminalStore } from '@/store';
|
||||
import { fontFamilyKey, fontSizeKey, fontWeightKey, fontFamilySuffix, cursorStyleKey } from '../../../types/terminal.const';
|
||||
import { labelFilter } from '@/types/form';
|
||||
@@ -112,7 +126,7 @@
|
||||
const { preference, updateTerminalPreference } = useTerminalStore();
|
||||
|
||||
const previewTerminal = ref();
|
||||
const formModel = ref<TerminalDisplaySetting>({ ...preference.displaySetting });
|
||||
const formModel = ref<TerminalDisplaySetting>({});
|
||||
|
||||
// 监听内容变化
|
||||
watch(formModel, (v) => {
|
||||
@@ -137,17 +151,24 @@
|
||||
previewTerminal.value.term.focus();
|
||||
}, { deep: true });
|
||||
|
||||
// 初始化配置
|
||||
onMounted(() => {
|
||||
formModel.value = { ...preference.displaySetting };
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@terminal-width: 458px;
|
||||
|
||||
.setting-body {
|
||||
height: 248px;
|
||||
height: 326px;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
:deep(.arco-form) {
|
||||
width: 412px;
|
||||
|
||||
.arco-form-item-label {
|
||||
user-select: none;
|
||||
}
|
||||
@@ -156,31 +177,10 @@
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.form-item-font-family {
|
||||
width: 158px;
|
||||
}
|
||||
|
||||
.form-item-font-size {
|
||||
width: 148px;
|
||||
}
|
||||
|
||||
.form-item-line-height {
|
||||
width: 114px;
|
||||
}
|
||||
|
||||
.form-item-font-weight, .form-item-font-bold-weight {
|
||||
width: 178px;
|
||||
}
|
||||
|
||||
.form-item-font-weight {
|
||||
margin-right: 70px;
|
||||
}
|
||||
|
||||
.form-item-cursor-style {
|
||||
margin-right: 90px;
|
||||
|
||||
.arco-radio-button-content {
|
||||
padding: 0 20px;
|
||||
padding: 0 24px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,11 @@
|
||||
</block-setting-item>
|
||||
</a-row>
|
||||
<a-row class="mb16" align="stretch" :gutter="16">
|
||||
<!-- unicode11 插件 -->
|
||||
<block-setting-item label="unicode11 插件" desc="支持 Unicode 11 字符集">
|
||||
<a-switch type="round"
|
||||
v-model="formModel.enableUnicodePlugin" />
|
||||
</block-setting-item>
|
||||
<!-- 图片渲染插件 -->
|
||||
<block-setting-item label="图片渲染插件" desc="支持使用 sixel 打开图片 (一般不需要开启)">
|
||||
<a-switch type="round"
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { TerminalThemeSchema } from '@/api/asset/host-terminal';
|
||||
import { Terminal } from 'xterm';
|
||||
import { Terminal } from '@xterm/xterm';
|
||||
import { onMounted, onUnmounted, ref } from 'vue';
|
||||
|
||||
const props = defineProps<{
|
||||
|
||||
@@ -8,57 +8,53 @@ export default class SftpTransferDownloader implements ISftpTransferDownloader {
|
||||
|
||||
public abort: boolean;
|
||||
|
||||
private blobArr: Array<Blob>;
|
||||
private client: WebSocket;
|
||||
private item: SftpTransferItem;
|
||||
|
||||
constructor(item: SftpTransferItem, client: WebSocket) {
|
||||
this.abort = false;
|
||||
this.blobArr = [];
|
||||
this.item = item;
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
// 开始下载
|
||||
startDownload() {
|
||||
initDownload() {
|
||||
this.item.status = TransferStatus.TRANSFERRING;
|
||||
// 发送开始下载信息
|
||||
this.client?.send(JSON.stringify({
|
||||
type: TransferOperatorType.DOWNLOAD_START,
|
||||
type: TransferOperatorType.DOWNLOAD_INIT,
|
||||
path: getPath(this.item.parentPath + '/' + this.item.name),
|
||||
hostId: this.item.hostId
|
||||
}));
|
||||
}
|
||||
|
||||
// 接收 blob
|
||||
resolveBlob(blob: Blob) {
|
||||
this.blobArr.push(blob);
|
||||
this.item.currentSize += blob.size;
|
||||
}
|
||||
|
||||
// 下载完成
|
||||
downloadFinish() {
|
||||
if (this.abort) {
|
||||
// 中断则不触发下载
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// 触发下载
|
||||
saveAs(new Blob(this.blobArr, {
|
||||
type: 'application/octet-stream'
|
||||
}), getFileName(this.item.name));
|
||||
// 设置实际大小
|
||||
this.item.currentSize = this.item.totalSize;
|
||||
if (this.item.totalSize === 0) {
|
||||
// 空文件直接触发下载
|
||||
try {
|
||||
// 触发下载
|
||||
saveAs(new Blob([], {
|
||||
type: 'application/octet-stream'
|
||||
}), getFileName(this.item.name));
|
||||
this.item.status = TransferStatus.SUCCESS;
|
||||
} catch (e) {
|
||||
this.item.status = TransferStatus.ERROR;
|
||||
this.item.errorMessage = '保存失败';
|
||||
}
|
||||
} else {
|
||||
this.item.status = TransferStatus.SUCCESS;
|
||||
} catch (e) {
|
||||
this.item.status = TransferStatus.ERROR;
|
||||
this.item.errorMessage = '保存失败';
|
||||
} finally {
|
||||
this.blobArr = [];
|
||||
}
|
||||
}
|
||||
|
||||
// 下载失败
|
||||
downloadError(msg: string | undefined) {
|
||||
this.blobArr = [];
|
||||
this.item.status = TransferStatus.ERROR;
|
||||
this.item.errorMessage = msg || '下载失败';
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { TransferReceiverType, TransferStatus, TransferType } from '../types/ter
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
import { getTerminalAccessToken, openHostTransferChannel } from '@/api/asset/host-terminal';
|
||||
import { nextId } from '@/utils';
|
||||
import { downloadWithTransferToken } from '@/api/asset/host-sftp';
|
||||
import SftpTransferUploader from './sftp-transfer-uploader';
|
||||
import SftpTransferDownloader from './sftp-transfer-downloader';
|
||||
|
||||
@@ -144,7 +145,9 @@ export default class SftpTransferManager implements ISftpTransferManager {
|
||||
// 计算传输进度
|
||||
private calcProgress() {
|
||||
this.transferList.forEach(item => {
|
||||
item.progress = (item.currentSize / item.totalSize * 100).toFixed(2);
|
||||
if (item.totalSize != 0) {
|
||||
item.progress = (item.currentSize / item.totalSize * 100).toFixed(2);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -165,7 +168,7 @@ export default class SftpTransferManager implements ISftpTransferManager {
|
||||
this.uploadFile();
|
||||
} else {
|
||||
// 下载
|
||||
this.uploadDownload();
|
||||
this.downloadFile();
|
||||
}
|
||||
} else {
|
||||
// 无任务关闭会话
|
||||
@@ -175,27 +178,28 @@ export default class SftpTransferManager implements ISftpTransferManager {
|
||||
|
||||
// 接收消息
|
||||
private async resolveMessage(message: MessageEvent) {
|
||||
if (message.data instanceof Blob) {
|
||||
// 二进制消息 下载数据
|
||||
this.resolveDownloadBlob(message.data);
|
||||
} else {
|
||||
// 文本消息
|
||||
const data = JSON.parse(message.data) as TransferOperatorResponse;
|
||||
if (data.type === TransferReceiverType.NEXT_TRANSFER
|
||||
|| data.type === TransferReceiverType.UPLOAD_FINISH
|
||||
|| data.type === TransferReceiverType.UPLOAD_ERROR) {
|
||||
// 执行下一个传输任务
|
||||
this.resolveNextTransfer(data);
|
||||
} else if (data.type === TransferReceiverType.UPLOAD_NEXT_BLOCK) {
|
||||
// 接收下一块上传数据
|
||||
await this.resolveUploadNextBlock();
|
||||
} else if (data.type === TransferReceiverType.DOWNLOAD_FINISH) {
|
||||
// 下载完成
|
||||
this.resolveDownloadFinish();
|
||||
} else if (data.type === TransferReceiverType.DOWNLOAD_ERROR) {
|
||||
// 下载失败
|
||||
this.resolveDownloadError(data.msg);
|
||||
}
|
||||
// 文本消息
|
||||
const data = JSON.parse(message.data) as TransferOperatorResponse;
|
||||
if (data.type === TransferReceiverType.NEXT_TRANSFER
|
||||
|| data.type === TransferReceiverType.UPLOAD_FINISH
|
||||
|| data.type === TransferReceiverType.UPLOAD_ERROR) {
|
||||
// 执行下一个传输任务
|
||||
this.resolveNextTransfer(data);
|
||||
} else if (data.type === TransferReceiverType.UPLOAD_NEXT_BLOCK) {
|
||||
// 接收下一块上传数据
|
||||
await this.resolveUploadNextBlock();
|
||||
} else if (data.type === TransferReceiverType.DOWNLOAD_START) {
|
||||
// 开始下载
|
||||
this.resolveDownloadStart(data);
|
||||
} else if (data.type === TransferReceiverType.DOWNLOAD_PROGRESS) {
|
||||
// 下载进度
|
||||
this.resolveDownloadProgress(data);
|
||||
} else if (data.type === TransferReceiverType.DOWNLOAD_FINISH) {
|
||||
// 下载完成
|
||||
this.resolveDownloadFinish();
|
||||
} else if (data.type === TransferReceiverType.DOWNLOAD_ERROR) {
|
||||
// 下载失败
|
||||
this.resolveDownloadError(data.msg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,11 +212,11 @@ export default class SftpTransferManager implements ISftpTransferManager {
|
||||
}
|
||||
|
||||
// 下载文件
|
||||
private uploadDownload() {
|
||||
private downloadFile() {
|
||||
// 创建下载器
|
||||
this.currentDownloader = new SftpTransferDownloader(this.currentItem as SftpTransferItem, this.client as WebSocket);
|
||||
// 开始下载
|
||||
this.currentDownloader.startDownload();
|
||||
// 初始化下载
|
||||
this.currentDownloader.initDownload();
|
||||
}
|
||||
|
||||
// 接收下一个传输任务响应
|
||||
@@ -251,9 +255,16 @@ export default class SftpTransferManager implements ISftpTransferManager {
|
||||
}
|
||||
}
|
||||
|
||||
// 接收下载数据
|
||||
private resolveDownloadBlob(blob: Blob) {
|
||||
this.currentDownloader?.resolveBlob(blob);
|
||||
// 接收开始下载响应
|
||||
private resolveDownloadStart(data: TransferOperatorResponse) {
|
||||
downloadWithTransferToken(data.channelId as string, data.transferToken as string);
|
||||
}
|
||||
|
||||
// 接收下载进度响应
|
||||
private resolveDownloadProgress(data: TransferOperatorResponse) {
|
||||
if (this.currentItem && data.currentSize) {
|
||||
this.currentItem.currentSize = data.currentSize;
|
||||
}
|
||||
}
|
||||
|
||||
// 接收下载完成响应
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { ShortcutKey, TerminalInteractSetting, TerminalShortcutKey } from '@/store/modules/terminal/types';
|
||||
import type { ISshSession, ISshSessionHandler, XtermDomRef } from '../types/terminal.type';
|
||||
import type { Terminal } from 'xterm';
|
||||
import type { Terminal } from '@xterm/xterm';
|
||||
import useCopy from '@/hooks/copy';
|
||||
import html2canvas from 'html2canvas';
|
||||
import { useTerminalStore, useUserStore } from '@/store';
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
import type { UnwrapRef } from 'vue';
|
||||
import type { ISearchOptions } from '@xterm/addon-search';
|
||||
import type { TerminalPreference } from '@/store/modules/terminal/types';
|
||||
import type { ISshSession, ISshSessionHandler, ITerminalChannel, XtermAddons, XtermDomRef } from '../types/terminal.type';
|
||||
import { useTerminalStore } from '@/store';
|
||||
import { InputProtocol } from '../types/terminal.protocol';
|
||||
import { fontFamilySuffix, TerminalShortcutType, TerminalStatus } from '../types/terminal.const';
|
||||
import { Terminal } from 'xterm';
|
||||
import { FitAddon } from 'xterm-addon-fit';
|
||||
import { WebLinksAddon } from 'xterm-addon-web-links';
|
||||
import { ISearchOptions, SearchAddon } from 'xterm-addon-search';
|
||||
import { ImageAddon } from 'xterm-addon-image';
|
||||
import { CanvasAddon } from 'xterm-addon-canvas';
|
||||
import { WebglAddon } from 'xterm-addon-webgl';
|
||||
import { Terminal } from '@xterm/xterm';
|
||||
import { FitAddon } from '@xterm/addon-fit';
|
||||
import { SearchAddon } from '@xterm/addon-search';
|
||||
import { WebLinksAddon } from '@xterm/addon-web-links';
|
||||
import { ImageAddon } from '@xterm/addon-image';
|
||||
import { Unicode11Addon } from '@xterm/addon-unicode11';
|
||||
import { CanvasAddon } from '@xterm/addon-canvas';
|
||||
import { WebglAddon } from '@xterm/addon-webgl';
|
||||
import { playBell } from '@/utils/bell';
|
||||
import { addEventListen } from '@/utils/event';
|
||||
import SshSessionHandler from './ssh-session-handler';
|
||||
@@ -63,9 +65,10 @@ export default class SshSession implements ISshSession {
|
||||
fastScrollModifier: !!preference.interactSetting.fastScrollModifier ? 'alt' : 'none',
|
||||
altClickMovesCursor: !!preference.interactSetting.altClickMovesCursor,
|
||||
rightClickSelectsWord: !!preference.interactSetting.rightClickSelectsWord,
|
||||
fontFamily: preference.displaySetting.fontFamily + fontFamilySuffix,
|
||||
wordSeparator: preference.interactSetting.wordSeparator,
|
||||
fontFamily: preference.displaySetting.fontFamily + fontFamilySuffix,
|
||||
scrollback: preference.sessionSetting.scrollBackLine,
|
||||
allowProposedApi: true,
|
||||
});
|
||||
// 处理器
|
||||
this.handler = new SshSessionHandler(this, domRef);
|
||||
@@ -192,12 +195,20 @@ export default class SshSession implements ISshSession {
|
||||
if (preference.pluginsSetting.enableImagePlugin) {
|
||||
this.addons.image = new ImageAddon();
|
||||
}
|
||||
// unicode11 插件
|
||||
if (preference.pluginsSetting.enableUnicodePlugin) {
|
||||
this.addons.unicode = new Unicode11Addon();
|
||||
}
|
||||
// 加载插件
|
||||
for (const addon of Object.values(this.addons)) {
|
||||
if (addon) {
|
||||
this.inst.loadAddon(addon);
|
||||
}
|
||||
}
|
||||
// 设置 unicode11 版本
|
||||
if (this.addons.unicode) {
|
||||
this.inst.unicode.activeVersion = '11';
|
||||
}
|
||||
}
|
||||
|
||||
// 设置已连接
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
import CommandSnippetDrawer from '@/views/host/command-snippet/components/command-snippet-drawer.vue';
|
||||
import PathBookmarkDrawer from '@/views/host/path-bookmark/components/path-bookmark-drawer.vue';
|
||||
import '@/assets/style/host-terminal-layout.less';
|
||||
import 'xterm/css/xterm.css';
|
||||
import '@xterm/xterm/css/xterm.css';
|
||||
|
||||
const terminalStore = useTerminalStore();
|
||||
const dictStore = useDictStore();
|
||||
|
||||
@@ -336,7 +336,7 @@ export const TransferOperatorType = {
|
||||
UPLOAD_START: 'uploadStart',
|
||||
UPLOAD_FINISH: 'uploadFinish',
|
||||
UPLOAD_ERROR: 'uploadError',
|
||||
DOWNLOAD_START: 'downloadStart',
|
||||
DOWNLOAD_INIT: 'downloadInit',
|
||||
DOWNLOAD_ABORT: 'downloadAbort',
|
||||
};
|
||||
|
||||
@@ -346,6 +346,8 @@ export const TransferReceiverType = {
|
||||
UPLOAD_NEXT_BLOCK: 'uploadNextBlock',
|
||||
UPLOAD_FINISH: 'uploadFinish',
|
||||
UPLOAD_ERROR: 'uploadError',
|
||||
DOWNLOAD_START: 'downloadStart',
|
||||
DOWNLOAD_PROGRESS: 'downloadProgress',
|
||||
DOWNLOAD_FINISH: 'downloadFinish',
|
||||
DOWNLOAD_ERROR: 'downloadError',
|
||||
};
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import type { Terminal } from 'xterm';
|
||||
import type { FitAddon } from 'xterm-addon-fit';
|
||||
import type { CanvasAddon } from 'xterm-addon-canvas';
|
||||
import type { WebglAddon } from 'xterm-addon-webgl';
|
||||
import type { WebLinksAddon } from 'xterm-addon-web-links';
|
||||
import type { ISearchOptions, SearchAddon } from 'xterm-addon-search';
|
||||
import type { ImageAddon } from 'xterm-addon-image';
|
||||
import type { Terminal } from '@xterm/xterm';
|
||||
import type { FitAddon } from '@xterm/addon-fit';
|
||||
import type { CanvasAddon } from '@xterm/addon-canvas';
|
||||
import type { WebglAddon } from '@xterm/addon-webgl';
|
||||
import type { WebLinksAddon } from '@xterm/addon-web-links';
|
||||
import type { ISearchOptions, SearchAddon } from '@xterm/addon-search';
|
||||
import type { ImageAddon } from '@xterm/addon-image';
|
||||
import type { Unicode11Addon } from '@xterm/addon-unicode11';
|
||||
import type { CSSProperties } from 'vue';
|
||||
import type { HostQueryResponse } from '@/api/asset/host';
|
||||
|
||||
@@ -225,6 +226,7 @@ export interface XtermAddons {
|
||||
weblink: WebLinksAddon;
|
||||
search: SearchAddon;
|
||||
image: ImageAddon;
|
||||
unicode: Unicode11Addon;
|
||||
}
|
||||
|
||||
// 终端会话定义
|
||||
@@ -430,10 +432,8 @@ export interface ISftpTransferUploader {
|
||||
export interface ISftpTransferDownloader {
|
||||
// 是否中断
|
||||
abort: boolean;
|
||||
// 开始下载
|
||||
startDownload: () => void;
|
||||
// 接收 blob
|
||||
resolveBlob: (blob: Blob) => void;
|
||||
// 初始化下载
|
||||
initDownload: () => void;
|
||||
// 下载完成
|
||||
downloadFinish: () => void;
|
||||
// 下载失败
|
||||
@@ -459,8 +459,11 @@ export interface SftpTransferItem {
|
||||
|
||||
// 传输操作响应
|
||||
export interface TransferOperatorResponse {
|
||||
channelId?: string;
|
||||
type: string;
|
||||
hostId?: number;
|
||||
currentSize?: number;
|
||||
transferToken?: string;
|
||||
success: boolean;
|
||||
msg?: string;
|
||||
}
|
||||
|
||||
@@ -126,8 +126,9 @@
|
||||
onOk: async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
// 调用删除
|
||||
await clearExecJobLog(formModel.value);
|
||||
// 调用清空
|
||||
const { data } = await clearExecJobLog(formModel.value);
|
||||
Message.success(`已成功清空 ${data} 条数据`);
|
||||
emits('clear');
|
||||
// 清空
|
||||
setVisible(false);
|
||||
|
||||
@@ -162,8 +162,9 @@
|
||||
onOk: async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
// 调用删除
|
||||
await clearOperatorLog(formModel.value);
|
||||
// 调用清空
|
||||
const { data } = await clearOperatorLog(formModel.value);
|
||||
Message.success(`已成功清空 ${data} 条数据`);
|
||||
emits('clear');
|
||||
// 清空
|
||||
setVisible(false);
|
||||
|
||||
Reference in New Issue
Block a user