🔨 sftp 表格.

This commit is contained in:
lijiahangmax
2024-02-06 22:26:44 +08:00
parent 9b79ebb6e6
commit 734f7a40cd
35 changed files with 1634 additions and 75 deletions

View File

@@ -3,10 +3,7 @@ package com.orion.ops.module.asset.handler.host.terminal.enums;
import com.alibaba.fastjson.JSONObject;
import com.orion.ops.module.asset.handler.host.terminal.handler.*;
import com.orion.ops.module.asset.handler.host.terminal.model.TerminalBasePayload;
import com.orion.ops.module.asset.handler.host.terminal.model.request.TerminalCheckRequest;
import com.orion.ops.module.asset.handler.host.terminal.model.request.TerminalConnectRequest;
import com.orion.ops.module.asset.handler.host.terminal.model.request.TerminalInputRequest;
import com.orion.ops.module.asset.handler.host.terminal.model.request.TerminalResizeRequest;
import com.orion.ops.module.asset.handler.host.terminal.model.request.*;
import com.orion.spring.SpringHolder;
import lombok.Getter;
import org.springframework.stereotype.Component;
@@ -55,27 +52,46 @@ public enum InputTypeEnum {
TerminalBasePayload.class),
/**
* 修改大小
* SSH 修改大小
*/
RESIZE("rs",
TerminalResizeHandler.class,
SSH_RESIZE("rs",
SshResizeHandler.class,
new String[]{"type", "sessionId", "cols", "rows"},
TerminalResizeRequest.class),
SshResizeRequest.class),
/**
* 输入
* SSH 输入
*/
INPUT("i",
TerminalInputHandler.class,
SSH_INPUT("i",
SshInputHandler.class,
new String[]{"type", "sessionId", "command"},
TerminalInputRequest.class),
SshInputRequest.class),
// LS
// MK
// RM
// MV
// TC
// CD
/**
* SFTP 文件列表
*/
SFTP_LIST("ls",
SftpListHandler.class,
new String[]{"type", "sessionId", "path"},
SftpListRequest.class),
// TODO
// MKDIR
// TOUCH
// REMOVE
// MOVE
// TRUNCATE
// CHMOD
// GET
// SAVE
// COPY
// UPLOAD
// DOWNLOAD
;

View File

@@ -36,9 +36,14 @@ public enum OutputTypeEnum {
PONG("p", "${type}"),
/**
* 输出
* SSH 输出
*/
OUTPUT("o", "${type}|${sessionId}|${body}"),
SSH_OUTPUT("o", "${type}|${sessionId}|${body}"),
/**
* SFTP 文件列表
*/
SFTP_LIST("ls", "${type}|${sessionId}|${result}|${path}|${body}"),
;

View File

@@ -0,0 +1,66 @@
package com.orion.ops.module.asset.handler.host.terminal.handler;
import com.alibaba.fastjson.JSON;
import com.orion.lang.utils.Strings;
import com.orion.ops.framework.common.enums.BooleanBit;
import com.orion.ops.module.asset.handler.host.terminal.enums.OutputTypeEnum;
import com.orion.ops.module.asset.handler.host.terminal.manager.TerminalManager;
import com.orion.ops.module.asset.handler.host.terminal.model.request.SftpListRequest;
import com.orion.ops.module.asset.handler.host.terminal.model.response.SftpFileResponse;
import com.orion.ops.module.asset.handler.host.terminal.model.response.SftpListResponse;
import com.orion.ops.module.asset.handler.host.terminal.session.ISftpSession;
import com.orion.ops.module.asset.handler.host.terminal.session.ITerminalSession;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.WebSocketSession;
import javax.annotation.Resource;
import java.util.List;
/**
* sftp 文件列表
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023/12/29 15:32
*/
@Slf4j
@Component
public class SftpListHandler extends AbstractTerminalHandler<SftpListRequest> {
@Resource
private TerminalManager terminalManager;
@Override
public void handle(WebSocketSession channel, SftpListRequest payload) {
// 获取会话
ITerminalSession session = terminalManager.getSession(channel.getId(), payload.getSessionId());
String path = payload.getPath();
log.info("SftpListHandler-handle session: {}, path: {}", payload.getSessionId(), path);
if (session instanceof ISftpSession) {
Exception ex = null;
List<SftpFileResponse> list = null;
try {
// 空目录则直接获取 home 目录
if (Strings.isBlank(path)) {
path = ((ISftpSession) session).getHome();
}
// 文件列表
list = ((ISftpSession) session).list(path);
} catch (Exception e) {
log.error("SftpListHandler-handle error", e);
ex = e;
}
// 返回
this.send(channel,
OutputTypeEnum.SFTP_LIST,
SftpListResponse.builder()
.sessionId(payload.getSessionId())
.result(BooleanBit.of(ex == null).getValue())
.path(path)
.body(list == null ? null : JSON.toJSONString(list))
.build());
}
}
}

View File

@@ -1,7 +1,7 @@
package com.orion.ops.module.asset.handler.host.terminal.handler;
import com.orion.ops.module.asset.handler.host.terminal.manager.TerminalManager;
import com.orion.ops.module.asset.handler.host.terminal.model.request.TerminalInputRequest;
import com.orion.ops.module.asset.handler.host.terminal.model.request.SshInputRequest;
import com.orion.ops.module.asset.handler.host.terminal.session.ISshSession;
import com.orion.ops.module.asset.handler.host.terminal.session.ITerminalSession;
import lombok.extern.slf4j.Slf4j;
@@ -11,7 +11,7 @@ import org.springframework.web.socket.WebSocketSession;
import javax.annotation.Resource;
/**
* 处理输入处理器
* ssh 处理输入处理器
*
* @author Jiahang Li
* @version 1.0.0
@@ -19,13 +19,13 @@ import javax.annotation.Resource;
*/
@Slf4j
@Component
public class TerminalInputHandler extends AbstractTerminalHandler<TerminalInputRequest> {
public class SshInputHandler extends AbstractTerminalHandler<SshInputRequest> {
@Resource
private TerminalManager terminalManager;
@Override
public void handle(WebSocketSession channel, TerminalInputRequest payload) {
public void handle(WebSocketSession channel, SshInputRequest payload) {
// 获取会话
ITerminalSession session = terminalManager.getSession(channel.getId(), payload.getSessionId());
if (session instanceof ISshSession) {

View File

@@ -1,7 +1,7 @@
package com.orion.ops.module.asset.handler.host.terminal.handler;
import com.orion.ops.module.asset.handler.host.terminal.manager.TerminalManager;
import com.orion.ops.module.asset.handler.host.terminal.model.request.TerminalResizeRequest;
import com.orion.ops.module.asset.handler.host.terminal.model.request.SshResizeRequest;
import com.orion.ops.module.asset.handler.host.terminal.session.ISshSession;
import com.orion.ops.module.asset.handler.host.terminal.session.ITerminalSession;
import lombok.extern.slf4j.Slf4j;
@@ -11,7 +11,7 @@ import org.springframework.web.socket.WebSocketSession;
import javax.annotation.Resource;
/**
* 修改大小处理器
* ssh 修改大小处理器
*
* @author Jiahang Li
* @version 1.0.0
@@ -19,13 +19,13 @@ import javax.annotation.Resource;
*/
@Slf4j
@Component
public class TerminalResizeHandler extends AbstractTerminalHandler<TerminalResizeRequest> {
public class SshResizeHandler extends AbstractTerminalHandler<SshResizeRequest> {
@Resource
private TerminalManager terminalManager;
@Override
public void handle(WebSocketSession channel, TerminalResizeRequest payload) {
public void handle(WebSocketSession channel, SshResizeRequest payload) {
// 获取会话
ITerminalSession session = terminalManager.getSession(channel.getId(), payload.getSessionId());
if (session instanceof ISshSession) {

View File

@@ -0,0 +1,34 @@
package com.orion.ops.module.asset.handler.host.terminal.model.request;
import com.orion.ops.module.asset.handler.host.terminal.model.TerminalBasePayload;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
/**
* sftp 修改文件权限 实体对象
* <p>
* i|eff00a1|path|mode
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024/2/6 13:31
*/
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
@Schema(name = "SftpChangeModeRequest", description = "sftp 修改文件权限 实体对象")
public class SftpChangeModeRequest extends TerminalBasePayload {
@Schema(description = "path")
private String path;
@Schema(description = "权限")
private String mode;
}

View File

@@ -0,0 +1,31 @@
package com.orion.ops.module.asset.handler.host.terminal.model.request;
import com.orion.ops.module.asset.handler.host.terminal.model.TerminalBasePayload;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
/**
* sftp 列表请求 实体对象
* <p>
* i|eff00a1|path
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024/2/6 13:31
*/
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
@Schema(name = "SftpListRequest", description = "sftp 列表请求 实体对象")
public class SftpListRequest extends TerminalBasePayload {
@Schema(description = "path")
private String path;
}

View File

@@ -0,0 +1,31 @@
package com.orion.ops.module.asset.handler.host.terminal.model.request;
import com.orion.ops.module.asset.handler.host.terminal.model.TerminalBasePayload;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
/**
* sftp 创建文件夹 实体对象
* <p>
* i|eff00a1|path
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024/2/6 13:31
*/
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
@Schema(name = "SftpMakeDirectoryRequest", description = "sftp 创建文件夹 实体对象")
public class SftpMakeDirectoryRequest extends TerminalBasePayload {
@Schema(description = "path")
private String path;
}

View File

@@ -0,0 +1,34 @@
package com.orion.ops.module.asset.handler.host.terminal.model.request;
import com.orion.ops.module.asset.handler.host.terminal.model.TerminalBasePayload;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
/**
* sftp 移动文件 实体对象
* <p>
* i|eff00a1|source|target
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024/2/6 13:31
*/
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
@Schema(name = "SftpMoveRequest", description = "sftp 移动文件 实体对象")
public class SftpMoveRequest extends TerminalBasePayload {
@Schema(description = "source")
private String source;
@Schema(description = "target")
private String target;
}

View File

@@ -0,0 +1,31 @@
package com.orion.ops.module.asset.handler.host.terminal.model.request;
import com.orion.ops.module.asset.handler.host.terminal.model.TerminalBasePayload;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
/**
* sftp 删除文件 实体对象
* <p>
* i|eff00a1|path
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024/2/6 13:31
*/
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
@Schema(name = "SftpRemoveRequest", description = "sftp 删除文件 实体对象")
public class SftpRemoveRequest extends TerminalBasePayload {
@Schema(description = "path")
private String path;
}

View File

@@ -0,0 +1,31 @@
package com.orion.ops.module.asset.handler.host.terminal.model.request;
import com.orion.ops.module.asset.handler.host.terminal.model.TerminalBasePayload;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
/**
* sftp 创建文件 实体对象
* <p>
* i|eff00a1|path
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024/2/6 13:31
*/
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
@Schema(name = "SftpTouchRequest", description = "sftp 创建文件 实体对象")
public class SftpTouchRequest extends TerminalBasePayload {
@Schema(description = "path")
private String path;
}

View File

@@ -0,0 +1,31 @@
package com.orion.ops.module.asset.handler.host.terminal.model.request;
import com.orion.ops.module.asset.handler.host.terminal.model.TerminalBasePayload;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
/**
* sftp 截断文件 实体对象
* <p>
* i|eff00a1|path
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024/2/6 13:31
*/
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
@Schema(name = "SftpTruncateRequest", description = "sftp 截断文件 实体对象")
public class SftpTruncateRequest extends TerminalBasePayload {
@Schema(description = "path")
private String path;
}

View File

@@ -9,7 +9,7 @@ import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
/**
* 输入请求 实体对象
* ssh 输入请求 实体对象
* <p>
* i|eff00a1|command
*
@@ -22,8 +22,8 @@ import lombok.experimental.SuperBuilder;
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
@Schema(name = "TerminalInputRequest", description = "输入请求 实体对象")
public class TerminalInputRequest extends TerminalBasePayload {
@Schema(name = "SshInputRequest", description = "ssh 输入请求 实体对象")
public class SshInputRequest extends TerminalBasePayload {
@Schema(description = "command")
private String command;

View File

@@ -9,7 +9,7 @@ import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
/**
* 修改大小请求 实体对象
* ssh 修改大小请求 实体对象
* <p>
* rs|eff00a1|100|20
*
@@ -22,8 +22,8 @@ import lombok.experimental.SuperBuilder;
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
@Schema(name = "TerminalResizeRequest", description = "修改大小请求 实体对象")
public class TerminalResizeRequest extends TerminalBasePayload {
@Schema(name = "SshResizeRequest", description = "ssh 修改大小请求 实体对象")
public class SshResizeRequest extends TerminalBasePayload {
@Schema(description = "列数")
private Integer cols;

View File

@@ -0,0 +1,58 @@
package com.orion.ops.module.asset.handler.host.terminal.model.response;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import java.util.Date;
/**
* sftp 文件响应 实体对象
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024/2/6 13:57
*/
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@Schema(name = "SftpFileResponse", description = "sftp 文件响应 实体对象")
public class SftpFileResponse {
@Schema(description = "名称")
private String name;
@Schema(description = "绝对路径")
private String path;
@Schema(description = "文件后缀")
private String suffix;
@Schema(description = "文件大小")
private String size;
@Schema(description = "文件大小(byte)")
private Long sizeByte;
@Schema(description = "属性")
private String attr;
@Schema(description = "是否为目录")
private Boolean isDir;
@Schema(description = "10进制表现的8进制权限")
private Integer permission;
@Schema(description = "用户id")
private Integer uid;
@Schema(description = "组id")
private Integer gid;
@Schema(description = "更新时间")
private Date modifyTime;
}

View File

@@ -0,0 +1,35 @@
package com.orion.ops.module.asset.handler.host.terminal.model.response;
import com.orion.ops.module.asset.handler.host.terminal.model.TerminalBasePayload;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
/**
* sftp 列表响应 实体对象
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024/2/6 16:20
*/
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
@Schema(name = "SftpListResponse", description = "sftp 列表响应 实体对象")
public class SftpListResponse extends TerminalBasePayload {
@Schema(description = "检查结果")
private Integer result;
@Schema(description = "path")
private String path;
@Schema(description = "body")
private String body;
}

View File

@@ -9,7 +9,7 @@ import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
/**
* 主机输出响应 实体对象
* ssh 输出响应 实体对象
*
* @author Jiahang Li
* @version 1.0.0
@@ -20,8 +20,8 @@ import lombok.experimental.SuperBuilder;
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
@Schema(name = "TerminalOutputResponse", description = "主机输出响应 实体对象")
public class TerminalOutputResponse extends TerminalBasePayload {
@Schema(name = "SshOutputResponse", description = "ssh 输出响应 实体对象")
public class SshOutputResponse extends TerminalBasePayload {
@Schema(description = "body")
private String body;

View File

@@ -1,5 +1,9 @@
package com.orion.ops.module.asset.handler.host.terminal.session;
import com.orion.ops.module.asset.handler.host.terminal.model.response.SftpFileResponse;
import java.util.List;
/**
* sftp 会话定义
*
@@ -14,4 +18,19 @@ public interface ISftpSession extends ITerminalSession {
*/
void connect();
/**
* 获取 home 路径
*
* @return homePath
*/
String getHome();
/**
* 文件列表
*
* @param path path
* @return list
*/
List<SftpFileResponse> list(String path);
}

View File

@@ -1,13 +1,23 @@
package com.orion.ops.module.asset.handler.host.terminal.session;
import com.orion.lang.utils.Strings;
import com.orion.lang.utils.convert.TypeStore;
import com.orion.lang.utils.io.FileType;
import com.orion.lang.utils.io.Files1;
import com.orion.lang.utils.io.Streams;
import com.orion.net.host.SessionStore;
import com.orion.net.host.sftp.SftpExecutor;
import com.orion.net.host.sftp.SftpFile;
import com.orion.ops.framework.common.constant.Const;
import com.orion.ops.module.asset.handler.host.terminal.model.TerminalConfig;
import com.orion.ops.module.asset.handler.host.terminal.model.response.SftpFileResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.socket.WebSocketSession;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
/**
* 终端 ssh 会话
*
@@ -40,6 +50,20 @@ public class SftpSession extends TerminalSession implements ISftpSession {
executor.connect();
}
@Override
public String getHome() {
return executor.getHome();
}
@Override
public List<SftpFileResponse> list(String path) {
// 查询文件
List<SftpFile> files = executor.listFilesFilter(path, f -> !f.getName().startsWith("."), false, true);
return files.stream()
.map(SftpSession::fileMapping)
.collect(Collectors.toList());
}
@Override
public void keepAlive() {
try {
@@ -56,4 +80,29 @@ public class SftpSession extends TerminalSession implements ISftpSession {
Streams.close(sessionStore);
}
/**
* 文件映射
*
* @param sftpFile sftpFile
* @return file
*/
private static SftpFileResponse fileMapping(SftpFile sftpFile) {
SftpFileResponse file = new SftpFileResponse();
file.setName(sftpFile.getName());
file.setPath(sftpFile.getPath());
file.setSuffix(Files1.getSuffix(sftpFile.getName()));
file.setSize(Files1.getSize(sftpFile.getSize()));
file.setSizeByte(sftpFile.getSize());
file.setPermission(sftpFile.getPermission());
file.setUid(sftpFile.getUid());
file.setGid(sftpFile.getGid());
file.setAttr(sftpFile.getPermissionString());
file.setModifyTime(sftpFile.getModifyTime());
Boolean isDir = Optional.ofNullable(FileType.of(file.getAttr()))
.map(FileType.DIRECTORY::equals)
.orElse(false);
file.setIsDir(isDir);
return file;
}
}

View File

@@ -10,7 +10,7 @@ import com.orion.ops.module.asset.handler.host.terminal.constant.TerminalMessage
import com.orion.ops.module.asset.handler.host.terminal.enums.OutputTypeEnum;
import com.orion.ops.module.asset.handler.host.terminal.model.TerminalConfig;
import com.orion.ops.module.asset.handler.host.terminal.model.response.TerminalCloseResponse;
import com.orion.ops.module.asset.handler.host.terminal.model.response.TerminalOutputResponse;
import com.orion.ops.module.asset.handler.host.terminal.model.response.SshOutputResponse;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.socket.WebSocketSession;
@@ -117,12 +117,12 @@ public class SshSession extends TerminalSession implements ISshSession {
while (channel.isOpen() && (read = in.read(bs)) != -1) {
String body = lastLine = new String(bs, 0, read, config.getCharset());
// 响应
TerminalOutputResponse resp = TerminalOutputResponse.builder()
.type(OutputTypeEnum.OUTPUT.getType())
SshOutputResponse resp = SshOutputResponse.builder()
.type(OutputTypeEnum.SSH_OUTPUT.getType())
.sessionId(sessionId)
.body(body)
.build();
WebSockets.sendText(channel, OutputTypeEnum.OUTPUT.format(resp));
WebSockets.sendText(channel, OutputTypeEnum.SSH_OUTPUT.format(resp));
}
} catch (IOException ex) {
log.error("terminal 读取流失败", ex);