refactor: 修改终端协议.
This commit is contained in:
@@ -1,9 +1,7 @@
|
|||||||
package com.orion.ops.module.asset.handler.host.terminal;
|
package com.orion.ops.module.asset.handler.host.terminal;
|
||||||
|
|
||||||
import com.alibaba.fastjson.JSON;
|
|
||||||
import com.orion.ops.framework.websocket.core.handler.TextWebSocketHandler;
|
import com.orion.ops.framework.websocket.core.handler.TextWebSocketHandler;
|
||||||
import com.orion.ops.module.asset.handler.host.terminal.entity.Message;
|
import com.orion.ops.module.asset.handler.host.terminal.enums.InputTypeEnum;
|
||||||
import com.orion.ops.module.asset.handler.host.terminal.enums.InputOperatorTypeEnum;
|
|
||||||
import com.orion.ops.module.asset.handler.host.terminal.manager.TerminalManager;
|
import com.orion.ops.module.asset.handler.host.terminal.manager.TerminalManager;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
@@ -30,11 +28,10 @@ public class TerminalMessageDispatcher extends TextWebSocketHandler {
|
|||||||
public void onMessage(WebSocketSession session, String payload) {
|
public void onMessage(WebSocketSession session, String payload) {
|
||||||
try {
|
try {
|
||||||
// 解析类型
|
// 解析类型
|
||||||
Message<?> message = JSON.parseObject(payload, Message.class);
|
InputTypeEnum type = InputTypeEnum.of(payload);
|
||||||
InputOperatorTypeEnum type = InputOperatorTypeEnum.of(message.getType());
|
|
||||||
if (type != null) {
|
if (type != null) {
|
||||||
// 处理消息
|
// 解析并处理消息
|
||||||
type.getHandler().process(session, message);
|
type.getHandler().handle(session, type.parse(payload));
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("TerminalDispatchHandler-handleMessage-error id: {}, msg: {}", session.getId(), payload, e);
|
log.error("TerminalDispatchHandler-handleMessage-error id: {}, msg: {}", session.getId(), payload, e);
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
package com.orion.ops.module.asset.handler.host.terminal.entity;
|
|
||||||
|
|
||||||
import com.alibaba.fastjson.annotation.JSONField;
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Builder;
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 消息体包装
|
|
||||||
*
|
|
||||||
* @author Jiahang Li
|
|
||||||
* @version 1.0.0
|
|
||||||
* @since 2023/12/29 16:24
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
@Builder
|
|
||||||
@NoArgsConstructor
|
|
||||||
@AllArgsConstructor
|
|
||||||
@Schema(name = "MessageWrapper", description = "消息体包装")
|
|
||||||
public class Message<T> {
|
|
||||||
|
|
||||||
@JSONField(name = "s")
|
|
||||||
@Schema(description = "会话id")
|
|
||||||
private String session;
|
|
||||||
|
|
||||||
@JSONField(name = "t")
|
|
||||||
@Schema(description = "消息类型")
|
|
||||||
private String type;
|
|
||||||
|
|
||||||
@JSONField(name = "b")
|
|
||||||
@Schema(description = "消息体")
|
|
||||||
private T body;
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,98 +0,0 @@
|
|||||||
package com.orion.ops.module.asset.handler.host.terminal.enums;
|
|
||||||
|
|
||||||
import com.orion.ops.module.asset.handler.host.terminal.handler.ITerminalHandler;
|
|
||||||
import com.orion.ops.module.asset.handler.host.terminal.handler.TerminalCheckHandler;
|
|
||||||
import com.orion.ops.module.asset.handler.host.terminal.handler.TerminalConnectHandler;
|
|
||||||
import com.orion.spring.SpringHolder;
|
|
||||||
import lombok.Getter;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 输入操作类型枚举
|
|
||||||
*
|
|
||||||
* @author Jiahang Li
|
|
||||||
* @version 1.0.0
|
|
||||||
* @since 2023/12/29 15:33
|
|
||||||
*/
|
|
||||||
public enum InputOperatorTypeEnum {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 主机连接检查 (临时 token 换实际 token / 检查权限)
|
|
||||||
*/
|
|
||||||
CHECK("ck", TerminalCheckHandler.class),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 连接主机
|
|
||||||
*/
|
|
||||||
CONNECT("co", TerminalConnectHandler.class),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 关闭连接
|
|
||||||
*/
|
|
||||||
CLOSE("cl", TerminalConnectHandler.class),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ping
|
|
||||||
*/
|
|
||||||
PING("p", TerminalConnectHandler.class),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 修改大小
|
|
||||||
*/
|
|
||||||
RESIZE("rs", TerminalConnectHandler.class),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 执行
|
|
||||||
*/
|
|
||||||
EXEC("e", TerminalConnectHandler.class),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 输入
|
|
||||||
*/
|
|
||||||
INPUT("i", TerminalConnectHandler.class),
|
|
||||||
|
|
||||||
;
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
private final String type;
|
|
||||||
|
|
||||||
private final Class<? extends ITerminalHandler> handlerBean;
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
private ITerminalHandler handler;
|
|
||||||
|
|
||||||
InputOperatorTypeEnum(String type, Class<? extends ITerminalHandler> handlerBean) {
|
|
||||||
this.type = type;
|
|
||||||
this.handlerBean = handlerBean;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static InputOperatorTypeEnum of(String type) {
|
|
||||||
if (type == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
for (InputOperatorTypeEnum value : values()) {
|
|
||||||
if (value.type.equals(type)) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 类型字段定义
|
|
||||||
*/
|
|
||||||
@Component
|
|
||||||
static class TypeFieldDefinition {
|
|
||||||
|
|
||||||
@PostConstruct
|
|
||||||
public void init() {
|
|
||||||
for (InputOperatorTypeEnum value : InputOperatorTypeEnum.values()) {
|
|
||||||
value.handler = SpringHolder.getBean(value.handlerBean);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,168 @@
|
|||||||
|
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.ITerminalHandler;
|
||||||
|
import com.orion.ops.module.asset.handler.host.terminal.handler.TerminalCheckHandler;
|
||||||
|
import com.orion.ops.module.asset.handler.host.terminal.handler.TerminalConnectHandler;
|
||||||
|
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.spring.SpringHolder;
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 输入操作类型枚举
|
||||||
|
*
|
||||||
|
* @author Jiahang Li
|
||||||
|
* @version 1.0.0
|
||||||
|
* @since 2023/12/29 15:33
|
||||||
|
*/
|
||||||
|
public enum InputTypeEnum {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主机连接检查 置换token / 检查权限
|
||||||
|
*/
|
||||||
|
CHECK("ck",
|
||||||
|
TerminalCheckHandler.class,
|
||||||
|
new String[]{"type", "session", "hostId"},
|
||||||
|
TerminalCheckRequest.class),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 连接主机
|
||||||
|
*/
|
||||||
|
CONNECT("co",
|
||||||
|
TerminalConnectHandler.class,
|
||||||
|
new String[]{"type", "session", "cols", "rows"},
|
||||||
|
TerminalConnectRequest.class),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关闭连接
|
||||||
|
*/
|
||||||
|
CLOSE("cl",
|
||||||
|
TerminalConnectHandler.class,
|
||||||
|
new String[]{"type", "session"},
|
||||||
|
TerminalConnectRequest.class),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ping
|
||||||
|
*/
|
||||||
|
PING("p",
|
||||||
|
TerminalConnectHandler.class,
|
||||||
|
new String[]{"type", "session"},
|
||||||
|
TerminalConnectRequest.class),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改大小
|
||||||
|
*/
|
||||||
|
RESIZE("rs",
|
||||||
|
TerminalConnectHandler.class,
|
||||||
|
new String[]{"type", "session", "cols", "rows"},
|
||||||
|
TerminalConnectRequest.class),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行
|
||||||
|
*/
|
||||||
|
EXEC("e",
|
||||||
|
TerminalConnectHandler.class,
|
||||||
|
new String[]{"type", "session", "command"},
|
||||||
|
TerminalConnectRequest.class),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 输入
|
||||||
|
*/
|
||||||
|
INPUT("i",
|
||||||
|
TerminalConnectHandler.class,
|
||||||
|
new String[]{"type", "session", "command"},
|
||||||
|
TerminalConnectRequest.class),
|
||||||
|
|
||||||
|
;
|
||||||
|
|
||||||
|
private static final char SEPARATOR = '|';
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private final String type;
|
||||||
|
|
||||||
|
private final Class<? extends ITerminalHandler<? extends TerminalBasePayload>> handlerBean;
|
||||||
|
|
||||||
|
private final String[] payloadDefine;
|
||||||
|
|
||||||
|
private final Class<? extends TerminalBasePayload> payloadClass;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private ITerminalHandler<? extends TerminalBasePayload> handler;
|
||||||
|
|
||||||
|
<T extends TerminalBasePayload> InputTypeEnum(String type,
|
||||||
|
Class<? extends ITerminalHandler<T>> handlerBean,
|
||||||
|
String[] payloadDefine,
|
||||||
|
Class<T> payloadClass) {
|
||||||
|
this.type = type;
|
||||||
|
this.handlerBean = handlerBean;
|
||||||
|
this.payloadDefine = payloadDefine;
|
||||||
|
this.payloadClass = payloadClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static InputTypeEnum of(String payload) {
|
||||||
|
if (payload == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (InputTypeEnum value : values()) {
|
||||||
|
if (payload.startsWith(value.type + SEPARATOR)) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析请求
|
||||||
|
*
|
||||||
|
* @param payload payload
|
||||||
|
* @param <T> T
|
||||||
|
* @return payload
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T extends TerminalBasePayload> T parse(String payload) {
|
||||||
|
JSONObject object = new JSONObject();
|
||||||
|
int curr = 0;
|
||||||
|
int len = payload.length();
|
||||||
|
for (int i = 0, pl = payloadDefine.length; i < pl; i++) {
|
||||||
|
if (i == pl - 1) {
|
||||||
|
// 最后一次
|
||||||
|
object.put(payloadDefine[i], payload.substring(curr, len));
|
||||||
|
} else {
|
||||||
|
// 非最后一次
|
||||||
|
StringBuilder tmp = new StringBuilder();
|
||||||
|
for (; curr < len; curr++) {
|
||||||
|
char c = payload.charAt(curr);
|
||||||
|
if (c == SEPARATOR) {
|
||||||
|
object.put(payloadDefine[i], tmp.toString());
|
||||||
|
curr++;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
tmp.append(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (T) object.toJavaObject(payloadClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 类型字段定义
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
static class TypeFieldDefinition {
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void init() {
|
||||||
|
for (InputTypeEnum value : InputTypeEnum.values()) {
|
||||||
|
value.handler = SpringHolder.getBean(value.handlerBean);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
package com.orion.ops.module.asset.handler.host.terminal.enums;
|
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Getter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 输出操作类型枚举
|
|
||||||
*
|
|
||||||
* @author Jiahang Li
|
|
||||||
* @version 1.0.0
|
|
||||||
* @since 2023/12/29 15:33
|
|
||||||
*/
|
|
||||||
@Getter
|
|
||||||
@AllArgsConstructor
|
|
||||||
public enum OutputOperatorTypeEnum {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 主机连接检查
|
|
||||||
*/
|
|
||||||
CHECK("ck"),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 主机连接
|
|
||||||
*/
|
|
||||||
CONNECT("co"),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* pong
|
|
||||||
*/
|
|
||||||
PONG("p"),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 输出
|
|
||||||
*/
|
|
||||||
OUTPUT("o"),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 发生错误
|
|
||||||
*/
|
|
||||||
ERROR("e"),
|
|
||||||
|
|
||||||
;
|
|
||||||
|
|
||||||
private final String type;
|
|
||||||
|
|
||||||
public static OutputOperatorTypeEnum of(String type) {
|
|
||||||
if (type == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
for (OutputOperatorTypeEnum value : values()) {
|
|
||||||
if (value.type.equals(type)) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
package com.orion.ops.module.asset.handler.host.terminal.enums;
|
||||||
|
|
||||||
|
import com.orion.lang.utils.json.matcher.ReplacementFormatters;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 输出操作类型枚举
|
||||||
|
*
|
||||||
|
* @author Jiahang Li
|
||||||
|
* @version 1.0.0
|
||||||
|
* @since 2023/12/29 15:33
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum OutputTypeEnum {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主机连接检查
|
||||||
|
*/
|
||||||
|
CHECK("ck", "${type}|${session}|${token}|${result}|${errorMessage}"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主机连接
|
||||||
|
*/
|
||||||
|
CONNECT("co", "${type}|${session}|${result}|${errorMessage}"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pong
|
||||||
|
*/
|
||||||
|
PONG("p", "${type}|${session}"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 输出
|
||||||
|
*/
|
||||||
|
OUTPUT("o", "${type}|${session}|${body}"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发生错误
|
||||||
|
*/
|
||||||
|
ERROR("e", "${type}|${session}"),
|
||||||
|
|
||||||
|
;
|
||||||
|
|
||||||
|
private static final char SEPARATOR = '|';
|
||||||
|
|
||||||
|
private final String type;
|
||||||
|
|
||||||
|
private final String template;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化
|
||||||
|
*
|
||||||
|
* @param o o
|
||||||
|
* @return 格式化
|
||||||
|
*/
|
||||||
|
public String format(Object o) {
|
||||||
|
return ReplacementFormatters.format(this.template, o);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static OutputTypeEnum of(String type) {
|
||||||
|
if (type == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (OutputTypeEnum value : values()) {
|
||||||
|
if (value.type.equals(type)) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,9 +1,8 @@
|
|||||||
package com.orion.ops.module.asset.handler.host.terminal.handler;
|
package com.orion.ops.module.asset.handler.host.terminal.handler;
|
||||||
|
|
||||||
import com.alibaba.fastjson.JSONObject;
|
|
||||||
import com.orion.ops.framework.websocket.core.utils.WebSockets;
|
import com.orion.ops.framework.websocket.core.utils.WebSockets;
|
||||||
import com.orion.ops.module.asset.handler.host.terminal.entity.Message;
|
import com.orion.ops.module.asset.handler.host.terminal.enums.OutputTypeEnum;
|
||||||
import com.orion.ops.module.asset.handler.host.terminal.enums.OutputOperatorTypeEnum;
|
import com.orion.ops.module.asset.handler.host.terminal.model.TerminalBasePayload;
|
||||||
import org.springframework.web.socket.WebSocketSession;
|
import org.springframework.web.socket.WebSocketSession;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -13,61 +12,20 @@ import org.springframework.web.socket.WebSocketSession;
|
|||||||
* @version 1.0.0
|
* @version 1.0.0
|
||||||
* @since 2023/12/29 18:59
|
* @since 2023/12/29 18:59
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractTerminalHandler<T> implements ITerminalHandler {
|
public abstract class AbstractTerminalHandler<T extends TerminalBasePayload> implements ITerminalHandler<T> {
|
||||||
|
|
||||||
/**
|
|
||||||
* 类型
|
|
||||||
*/
|
|
||||||
private final Class<T> convert;
|
|
||||||
|
|
||||||
public AbstractTerminalHandler(Class<T> convert) {
|
|
||||||
this.convert = convert;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public void process(WebSocketSession session, Message<?> message) {
|
|
||||||
Message<T> res = (Message<T>) message;
|
|
||||||
res.setBody(((JSONObject) message.getBody()).toJavaObject(convert));
|
|
||||||
this.handle(session, res);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 处理消息
|
|
||||||
*
|
|
||||||
* @param session session
|
|
||||||
* @param msg msg
|
|
||||||
*/
|
|
||||||
protected abstract void handle(WebSocketSession session, Message<T> msg);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取响应结构
|
|
||||||
*
|
|
||||||
* @param in in
|
|
||||||
* @param type type
|
|
||||||
* @param body body
|
|
||||||
* @param <E> E
|
|
||||||
* @return out
|
|
||||||
*/
|
|
||||||
public <E> Message<E> out(Message<?> in, OutputOperatorTypeEnum type, E body) {
|
|
||||||
return Message.<E>builder()
|
|
||||||
.session(in.getSession())
|
|
||||||
.type(type.getType())
|
|
||||||
.body(body)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 发送消息
|
* 发送消息
|
||||||
*
|
*
|
||||||
* @param session session
|
* @param session session
|
||||||
* @param in in
|
|
||||||
* @param type type
|
* @param type type
|
||||||
* @param body body
|
* @param body body
|
||||||
|
* @param <E> E
|
||||||
*/
|
*/
|
||||||
public void send(WebSocketSession session, Message<?> in, OutputOperatorTypeEnum type, Object body) {
|
public <E extends TerminalBasePayload> void send(WebSocketSession session, OutputTypeEnum type, E body) {
|
||||||
|
body.setType(type.getType());
|
||||||
// 发送消息
|
// 发送消息
|
||||||
this.send(session, this.out(in, type, body));
|
this.send(session, type.format(body));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -76,8 +34,8 @@ public abstract class AbstractTerminalHandler<T> implements ITerminalHandler {
|
|||||||
* @param session session
|
* @param session session
|
||||||
* @param message message
|
* @param message message
|
||||||
*/
|
*/
|
||||||
protected void send(WebSocketSession session, Message<?> message) {
|
protected void send(WebSocketSession session, String message) {
|
||||||
WebSockets.sendJson(session, message);
|
WebSockets.sendText(session, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package com.orion.ops.module.asset.handler.host.terminal.handler;
|
package com.orion.ops.module.asset.handler.host.terminal.handler;
|
||||||
|
|
||||||
import com.orion.ops.module.asset.handler.host.terminal.entity.Message;
|
import com.orion.ops.module.asset.handler.host.terminal.model.TerminalBasePayload;
|
||||||
import org.springframework.web.socket.WebSocketSession;
|
import org.springframework.web.socket.WebSocketSession;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -10,14 +10,14 @@ import org.springframework.web.socket.WebSocketSession;
|
|||||||
* @version 1.0.0
|
* @version 1.0.0
|
||||||
* @since 2023/12/29 18:53
|
* @since 2023/12/29 18:53
|
||||||
*/
|
*/
|
||||||
public interface ITerminalHandler {
|
public interface ITerminalHandler<T extends TerminalBasePayload> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理消息
|
* 处理消息
|
||||||
*
|
*
|
||||||
* @param session session
|
* @param session session
|
||||||
* @param message message
|
* @param payload payload
|
||||||
*/
|
*/
|
||||||
void process(WebSocketSession session, Message<?> message);
|
void handle(WebSocketSession session, T payload);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,10 +15,9 @@ import com.orion.ops.module.asset.entity.dto.HostTerminalConnectDTO;
|
|||||||
import com.orion.ops.module.asset.entity.request.host.HostConnectLogCreateRequest;
|
import com.orion.ops.module.asset.entity.request.host.HostConnectLogCreateRequest;
|
||||||
import com.orion.ops.module.asset.enums.HostConnectStatusEnum;
|
import com.orion.ops.module.asset.enums.HostConnectStatusEnum;
|
||||||
import com.orion.ops.module.asset.enums.HostConnectTypeEnum;
|
import com.orion.ops.module.asset.enums.HostConnectTypeEnum;
|
||||||
import com.orion.ops.module.asset.handler.host.terminal.entity.Message;
|
import com.orion.ops.module.asset.handler.host.terminal.enums.OutputTypeEnum;
|
||||||
import com.orion.ops.module.asset.handler.host.terminal.entity.request.TerminalCheckRequest;
|
import com.orion.ops.module.asset.handler.host.terminal.model.request.TerminalCheckRequest;
|
||||||
import com.orion.ops.module.asset.handler.host.terminal.entity.response.TerminalCheckResponse;
|
import com.orion.ops.module.asset.handler.host.terminal.model.response.TerminalCheckResponse;
|
||||||
import com.orion.ops.module.asset.handler.host.terminal.enums.OutputOperatorTypeEnum;
|
|
||||||
import com.orion.ops.module.asset.service.HostConnectLogService;
|
import com.orion.ops.module.asset.service.HostConnectLogService;
|
||||||
import com.orion.ops.module.asset.service.HostTerminalService;
|
import com.orion.ops.module.asset.service.HostTerminalService;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@@ -51,25 +50,26 @@ public class TerminalCheckHandler extends AbstractTerminalHandler<TerminalCheckR
|
|||||||
@Resource
|
@Resource
|
||||||
private OperatorLogFrameworkService operatorLogFrameworkService;
|
private OperatorLogFrameworkService operatorLogFrameworkService;
|
||||||
|
|
||||||
public TerminalCheckHandler() {
|
|
||||||
super(TerminalCheckRequest.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void handle(WebSocketSession session, Message<TerminalCheckRequest> msg) {
|
public void handle(WebSocketSession session, TerminalCheckRequest payload) {
|
||||||
Long hostId = msg.getBody().getHostId();
|
Long hostId = payload.getHostId();
|
||||||
Long userId = this.getAttr(session, ExtraFieldConst.USER_ID);
|
Long userId = this.getAttr(session, ExtraFieldConst.USER_ID);
|
||||||
long startTime = System.currentTimeMillis();
|
long startTime = System.currentTimeMillis();
|
||||||
String terminalToken = UUIds.random15();
|
String token = UUIds.random15();
|
||||||
log.info("TerminalCheckHandler-handle start userId: {}, hostId: {}, terminalToken: {}", userId, hostId, terminalToken);
|
log.info("TerminalCheckHandler-handle start userId: {}, hostId: {}, token: {}", userId, hostId, token);
|
||||||
// 查询主机信息
|
// 查询主机信息
|
||||||
HostDO host = hostDAO.selectById(hostId);
|
HostDO host = hostDAO.selectById(hostId);
|
||||||
// 不存在返回错误信息
|
// 不存在返回错误信息
|
||||||
if (host == null) {
|
if (host == null) {
|
||||||
log.info("TerminalCheckHandler-handle unknown host userId: {}, hostId: {}", userId, hostId);
|
log.info("TerminalCheckHandler-handle unknown host userId: {}, hostId: {}", userId, hostId);
|
||||||
this.send(session, msg,
|
this.send(session,
|
||||||
OutputOperatorTypeEnum.CHECK,
|
OutputTypeEnum.CHECK,
|
||||||
new TerminalCheckResponse(terminalToken, BooleanBit.FALSE.getValue(), ErrorMessage.HOST_ABSENT));
|
TerminalCheckResponse.builder()
|
||||||
|
.session(payload.getSession())
|
||||||
|
.token(token)
|
||||||
|
.result(BooleanBit.FALSE.getValue())
|
||||||
|
.errorMessage(ErrorMessage.HOST_ABSENT)
|
||||||
|
.build());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Exception ex = null;
|
Exception ex = null;
|
||||||
@@ -77,19 +77,20 @@ public class TerminalCheckHandler extends AbstractTerminalHandler<TerminalCheckR
|
|||||||
// 获取连接信息
|
// 获取连接信息
|
||||||
HostTerminalConnectDTO connect = hostTerminalService.getTerminalConnectInfo(userId, host);
|
HostTerminalConnectDTO connect = hostTerminalService.getTerminalConnectInfo(userId, host);
|
||||||
// 设置到缓存中
|
// 设置到缓存中
|
||||||
session.getAttributes().put(terminalToken, connect);
|
session.getAttributes().put(token, connect);
|
||||||
log.info("TerminalCheckHandler-handle success userId: {}, hostId: {}, token: {}", userId, hostId, terminalToken);
|
log.info("TerminalCheckHandler-handle success userId: {}, hostId: {}, token: {}", userId, hostId, token);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
ex = e;
|
ex = e;
|
||||||
log.error("TerminalCheckHandler-handle error userId: {}, hostId: {}, token: {}", userId, hostId, terminalToken, e);
|
log.error("TerminalCheckHandler-handle error userId: {}, hostId: {}, token: {}", userId, hostId, token, e);
|
||||||
}
|
}
|
||||||
// 记录主机日志
|
// 记录主机日志
|
||||||
this.saveTerminalLog(session, userId, host, startTime, ex, terminalToken);
|
this.saveTerminalLog(session, userId, host, startTime, ex, token);
|
||||||
// 响应检查结果
|
// 响应检查结果
|
||||||
this.send(session, msg,
|
this.send(session,
|
||||||
OutputOperatorTypeEnum.CHECK,
|
OutputTypeEnum.CHECK,
|
||||||
TerminalCheckResponse.builder()
|
TerminalCheckResponse.builder()
|
||||||
.session(terminalToken)
|
.session(payload.getSession())
|
||||||
|
.token(token)
|
||||||
.result(BooleanBit.of(ex == null).getValue())
|
.result(BooleanBit.of(ex == null).getValue())
|
||||||
.errorMessage(ex == null ? null : ex.getMessage())
|
.errorMessage(ex == null ? null : ex.getMessage())
|
||||||
.build());
|
.build());
|
||||||
|
|||||||
@@ -12,11 +12,11 @@ import com.orion.ops.framework.common.enums.BooleanBit;
|
|||||||
import com.orion.ops.module.asset.entity.dto.HostTerminalConnectDTO;
|
import com.orion.ops.module.asset.entity.dto.HostTerminalConnectDTO;
|
||||||
import com.orion.ops.module.asset.enums.HostConnectStatusEnum;
|
import com.orion.ops.module.asset.enums.HostConnectStatusEnum;
|
||||||
import com.orion.ops.module.asset.handler.host.terminal.constant.TerminalMessage;
|
import com.orion.ops.module.asset.handler.host.terminal.constant.TerminalMessage;
|
||||||
import com.orion.ops.module.asset.handler.host.terminal.entity.Message;
|
import com.orion.ops.module.asset.handler.host.terminal.enums.OutputTypeEnum;
|
||||||
import com.orion.ops.module.asset.handler.host.terminal.entity.request.TerminalConnectRequest;
|
|
||||||
import com.orion.ops.module.asset.handler.host.terminal.entity.response.TerminalConnectResponse;
|
|
||||||
import com.orion.ops.module.asset.handler.host.terminal.enums.OutputOperatorTypeEnum;
|
|
||||||
import com.orion.ops.module.asset.handler.host.terminal.manager.TerminalManager;
|
import com.orion.ops.module.asset.handler.host.terminal.manager.TerminalManager;
|
||||||
|
import com.orion.ops.module.asset.handler.host.terminal.model.TerminalConfig;
|
||||||
|
import com.orion.ops.module.asset.handler.host.terminal.model.request.TerminalConnectRequest;
|
||||||
|
import com.orion.ops.module.asset.handler.host.terminal.model.response.TerminalConnectResponse;
|
||||||
import com.orion.ops.module.asset.handler.host.terminal.session.TerminalSession;
|
import com.orion.ops.module.asset.handler.host.terminal.session.TerminalSession;
|
||||||
import com.orion.ops.module.asset.service.HostConnectLogService;
|
import com.orion.ops.module.asset.service.HostConnectLogService;
|
||||||
import com.orion.ops.module.asset.service.HostTerminalService;
|
import com.orion.ops.module.asset.service.HostTerminalService;
|
||||||
@@ -46,21 +46,21 @@ public class TerminalConnectHandler extends AbstractTerminalHandler<TerminalConn
|
|||||||
@Resource
|
@Resource
|
||||||
private TerminalManager terminalManager;
|
private TerminalManager terminalManager;
|
||||||
|
|
||||||
public TerminalConnectHandler() {
|
|
||||||
super(TerminalConnectRequest.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void handle(WebSocketSession session, Message<TerminalConnectRequest> msg) {
|
public void handle(WebSocketSession session, TerminalConnectRequest payload) {
|
||||||
String token = msg.getSession();
|
String token = payload.getSession();
|
||||||
log.info("TerminalConnectHandler-handle start token: {}", token);
|
log.info("TerminalConnectHandler-handle start token: {}", token);
|
||||||
// 获取主机连接信息
|
// 获取主机连接信息
|
||||||
HostTerminalConnectDTO connect = this.getAttr(session, token);
|
HostTerminalConnectDTO connect = this.getAttr(session, token);
|
||||||
if (connect == null) {
|
if (connect == null) {
|
||||||
log.info("TerminalConnectHandler-handle unknown token: {}", token);
|
log.info("TerminalConnectHandler-handle unknown token: {}", token);
|
||||||
this.send(session, msg,
|
this.send(session,
|
||||||
OutputOperatorTypeEnum.CONNECT,
|
OutputTypeEnum.CONNECT,
|
||||||
new TerminalConnectResponse(BooleanBit.FALSE.getValue(), ErrorMessage.SESSION_ABSENT));
|
TerminalConnectResponse.builder()
|
||||||
|
.session(payload.getSession())
|
||||||
|
.result(BooleanBit.FALSE.getValue())
|
||||||
|
.errorMessage(ErrorMessage.SESSION_ABSENT)
|
||||||
|
.build());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 移除会话连接信息
|
// 移除会话连接信息
|
||||||
@@ -68,7 +68,7 @@ public class TerminalConnectHandler extends AbstractTerminalHandler<TerminalConn
|
|||||||
Exception ex = null;
|
Exception ex = null;
|
||||||
try {
|
try {
|
||||||
// 连接主机
|
// 连接主机
|
||||||
TerminalSession terminalSession = this.connect(token, connect, session, msg.getBody());
|
TerminalSession terminalSession = this.connect(token, connect, session, payload);
|
||||||
// 添加会话到 manager
|
// 添加会话到 manager
|
||||||
terminalManager.addSession(terminalSession);
|
terminalManager.addSession(terminalSession);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -77,9 +77,10 @@ public class TerminalConnectHandler extends AbstractTerminalHandler<TerminalConn
|
|||||||
hostConnectLogService.updateStatusByToken(token, HostConnectStatusEnum.FAILED);
|
hostConnectLogService.updateStatusByToken(token, HostConnectStatusEnum.FAILED);
|
||||||
}
|
}
|
||||||
// 返回连接状态
|
// 返回连接状态
|
||||||
this.send(session, msg,
|
this.send(session,
|
||||||
OutputOperatorTypeEnum.CONNECT,
|
OutputTypeEnum.CONNECT,
|
||||||
TerminalConnectResponse.builder()
|
TerminalConnectResponse.builder()
|
||||||
|
.session(payload.getSession())
|
||||||
.result(BooleanBit.of(ex == null).getValue())
|
.result(BooleanBit.of(ex == null).getValue())
|
||||||
.errorMessage(this.getConnectErrorMessage(ex))
|
.errorMessage(this.getConnectErrorMessage(ex))
|
||||||
.build());
|
.build());
|
||||||
@@ -100,9 +101,15 @@ public class TerminalConnectHandler extends AbstractTerminalHandler<TerminalConn
|
|||||||
TerminalConnectRequest body) {
|
TerminalConnectRequest body) {
|
||||||
TerminalSession terminalSession = null;
|
TerminalSession terminalSession = null;
|
||||||
try {
|
try {
|
||||||
|
// 连接配置
|
||||||
|
TerminalConfig config = TerminalConfig.builder()
|
||||||
|
.charset(connect.getCharset())
|
||||||
|
.fileNameCharset(connect.getFileNameCharset())
|
||||||
|
.fileContentCharset(connect.getFileContentCharset())
|
||||||
|
.build();
|
||||||
// 建立连接
|
// 建立连接
|
||||||
SessionStore sessionStore = hostTerminalService.openSessionStore(connect);
|
SessionStore sessionStore = hostTerminalService.openSessionStore(connect);
|
||||||
terminalSession = new TerminalSession(token, session, sessionStore);
|
terminalSession = new TerminalSession(token, session, sessionStore, config);
|
||||||
terminalSession.connect(body.getCols(), body.getRows());
|
terminalSession.connect(body.getCols(), body.getRows());
|
||||||
log.info("TerminalConnectHandler-handle success token: {}", token);
|
log.info("TerminalConnectHandler-handle success token: {}", token);
|
||||||
return terminalSession;
|
return terminalSession;
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package com.orion.ops.module.asset.handler.host.terminal.model;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.experimental.SuperBuilder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 终端基础 payload
|
||||||
|
*
|
||||||
|
* @author Jiahang Li
|
||||||
|
* @version 1.0.0
|
||||||
|
* @since 2024/1/3 21:51
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@SuperBuilder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Schema(name = "TerminalBasePayload", description = "终端基础 payload")
|
||||||
|
public class TerminalBasePayload {
|
||||||
|
|
||||||
|
@Schema(description = "会话id")
|
||||||
|
private String session;
|
||||||
|
|
||||||
|
@Schema(description = "消息类型")
|
||||||
|
private String type;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package com.orion.ops.module.asset.handler.host.terminal.model;
|
||||||
|
|
||||||
|
import com.orion.ops.framework.desensitize.core.annotation.DesensitizeObject;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主机终端连接参数
|
||||||
|
*
|
||||||
|
* @author Jiahang Li
|
||||||
|
* @version 1.0.0
|
||||||
|
* @since 2024/1/3 23:30
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@DesensitizeObject
|
||||||
|
@Schema(name = "TerminalConfig", description = "主机终端连接参数")
|
||||||
|
public class TerminalConfig {
|
||||||
|
|
||||||
|
@Schema(description = "cols")
|
||||||
|
private Integer cols;
|
||||||
|
|
||||||
|
@Schema(description = "rows")
|
||||||
|
private Integer rows;
|
||||||
|
|
||||||
|
@Schema(description = "SSH输出编码")
|
||||||
|
private String charset;
|
||||||
|
|
||||||
|
@Schema(description = "文件名称编码")
|
||||||
|
private String fileNameCharset;
|
||||||
|
|
||||||
|
@Schema(description = "文件内容编码")
|
||||||
|
private String fileContentCharset;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,29 +1,30 @@
|
|||||||
package com.orion.ops.module.asset.handler.host.terminal.entity.request;
|
package com.orion.ops.module.asset.handler.host.terminal.model.request;
|
||||||
|
|
||||||
import com.alibaba.fastjson.annotation.JSONField;
|
import com.orion.ops.module.asset.handler.host.terminal.model.TerminalBasePayload;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Builder;
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.experimental.SuperBuilder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 主机连接检查请求 实体对象
|
* 主机连接检查请求 实体对象
|
||||||
|
* <p>
|
||||||
|
* ck|eff00a1|1
|
||||||
*
|
*
|
||||||
* @author Jiahang Li
|
* @author Jiahang Li
|
||||||
* @version 1.0.0
|
* @version 1.0.0
|
||||||
* @since 2023/12/29 16:20
|
* @since 2023/12/29 16:20
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@Builder
|
@SuperBuilder
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
@Schema(name = "TerminalConnectRequest", description = "主机连接检查请求 实体对象")
|
@Schema(name = "TerminalConnectRequest", description = "主机连接检查请求 实体对象")
|
||||||
public class TerminalCheckRequest {
|
public class TerminalCheckRequest extends TerminalBasePayload {
|
||||||
|
|
||||||
// 连接主机 {"t":"ck","s": "1001","b":{"h":1}}
|
|
||||||
|
|
||||||
@JSONField(name = "h")
|
|
||||||
@Schema(description = "主机id")
|
@Schema(description = "主机id")
|
||||||
private Long hostId;
|
private Long hostId;
|
||||||
|
|
||||||
@@ -1,33 +1,33 @@
|
|||||||
package com.orion.ops.module.asset.handler.host.terminal.entity.request;
|
package com.orion.ops.module.asset.handler.host.terminal.model.request;
|
||||||
|
|
||||||
import com.alibaba.fastjson.annotation.JSONField;
|
import com.orion.ops.module.asset.handler.host.terminal.model.TerminalBasePayload;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Builder;
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.experimental.SuperBuilder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 终端连接请求 实体对象
|
* 终端连接请求 实体对象
|
||||||
|
* <p>
|
||||||
|
* co|eff00a1|100|20
|
||||||
*
|
*
|
||||||
* @author Jiahang Li
|
* @author Jiahang Li
|
||||||
* @version 1.0.0
|
* @version 1.0.0
|
||||||
* @since 2023/12/29 16:20
|
* @since 2023/12/29 16:20
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@Builder
|
@SuperBuilder
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
@Schema(name = "TerminalConnectRequest", description = "终端连接请求 实体对象")
|
@Schema(name = "TerminalConnectRequest", description = "终端连接请求 实体对象")
|
||||||
public class TerminalConnectRequest {
|
public class TerminalConnectRequest extends TerminalBasePayload {
|
||||||
|
|
||||||
// 连接主机 {"t":"co","s": "1001","b":{"c":100,"r":20}}
|
|
||||||
|
|
||||||
@JSONField(name = "c")
|
|
||||||
@Schema(description = "列数")
|
@Schema(description = "列数")
|
||||||
private Integer cols;
|
private Integer cols;
|
||||||
|
|
||||||
@JSONField(name = "r")
|
|
||||||
@Schema(description = "行数")
|
@Schema(description = "行数")
|
||||||
private Integer rows;
|
private Integer rows;
|
||||||
|
|
||||||
@@ -1,33 +1,33 @@
|
|||||||
package com.orion.ops.module.asset.handler.host.terminal.entity.request;
|
package com.orion.ops.module.asset.handler.host.terminal.model.request;
|
||||||
|
|
||||||
import com.alibaba.fastjson.annotation.JSONField;
|
import com.orion.ops.module.asset.handler.host.terminal.model.TerminalBasePayload;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Builder;
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.experimental.SuperBuilder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 修改大小请求 实体对象
|
* 修改大小请求 实体对象
|
||||||
|
* <p>
|
||||||
|
* rs|eff00a1|100|20
|
||||||
*
|
*
|
||||||
* @author Jiahang Li
|
* @author Jiahang Li
|
||||||
* @version 1.0.0
|
* @version 1.0.0
|
||||||
* @since 2023/12/29 16:20
|
* @since 2023/12/29 16:20
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@Builder
|
@SuperBuilder
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
@Schema(name = "TerminalResizeRequest", description = "修改大小请求 实体对象")
|
@Schema(name = "TerminalResizeRequest", description = "修改大小请求 实体对象")
|
||||||
public class TerminalResizeRequest {
|
public class TerminalResizeRequest extends TerminalBasePayload {
|
||||||
|
|
||||||
// 连接主机 {"t":"rs","s": "1001","b":{"c":100,"r":20}}
|
|
||||||
|
|
||||||
@JSONField(name = "c")
|
|
||||||
@Schema(description = "列数")
|
@Schema(description = "列数")
|
||||||
private Integer cols;
|
private Integer cols;
|
||||||
|
|
||||||
@JSONField(name = "r")
|
|
||||||
@Schema(description = "行数")
|
@Schema(description = "行数")
|
||||||
private Integer rows;
|
private Integer rows;
|
||||||
|
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
package com.orion.ops.module.asset.handler.host.terminal.entity.response;
|
package com.orion.ops.module.asset.handler.host.terminal.model.response;
|
||||||
|
|
||||||
import com.alibaba.fastjson.annotation.JSONField;
|
import com.orion.ops.module.asset.handler.host.terminal.model.TerminalBasePayload;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Builder;
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.experimental.SuperBuilder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 主机连接检查响应 实体对象
|
* 主机连接检查响应 实体对象
|
||||||
@@ -15,21 +16,19 @@ import lombok.NoArgsConstructor;
|
|||||||
* @since 2023/12/29 16:20
|
* @since 2023/12/29 16:20
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@Builder
|
@SuperBuilder
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
@Schema(name = "TerminalCheckResponse", description = "主机连接检查响应 实体对象")
|
@Schema(name = "TerminalCheckResponse", description = "主机连接检查响应 实体对象")
|
||||||
public class TerminalCheckResponse {
|
public class TerminalCheckResponse extends TerminalBasePayload {
|
||||||
|
|
||||||
@JSONField(name = "s")
|
@Schema(description = "token")
|
||||||
@Schema(description = "会话id")
|
private String token;
|
||||||
private String session;
|
|
||||||
|
|
||||||
@JSONField(name = "r")
|
|
||||||
@Schema(description = "检查结果")
|
@Schema(description = "检查结果")
|
||||||
private Integer result;
|
private Integer result;
|
||||||
|
|
||||||
@JSONField(name = "em")
|
|
||||||
@Schema(description = "错误信息")
|
@Schema(description = "错误信息")
|
||||||
private String errorMessage;
|
private String errorMessage;
|
||||||
|
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
package com.orion.ops.module.asset.handler.host.terminal.entity.response;
|
package com.orion.ops.module.asset.handler.host.terminal.model.response;
|
||||||
|
|
||||||
import com.alibaba.fastjson.annotation.JSONField;
|
import com.orion.ops.module.asset.handler.host.terminal.model.TerminalBasePayload;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Builder;
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.experimental.SuperBuilder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 终端连接响应 实体对象
|
* 终端连接响应 实体对象
|
||||||
@@ -15,17 +16,16 @@ import lombok.NoArgsConstructor;
|
|||||||
* @since 2023/12/29 16:20
|
* @since 2023/12/29 16:20
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@Builder
|
@SuperBuilder
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
@Schema(name = "TerminalConnectResponse", description = "终端连接响应 实体对象")
|
@Schema(name = "TerminalConnectResponse", description = "终端连接响应 实体对象")
|
||||||
public class TerminalConnectResponse {
|
public class TerminalConnectResponse extends TerminalBasePayload {
|
||||||
|
|
||||||
@JSONField(name = "r")
|
|
||||||
@Schema(description = "检查结果")
|
@Schema(description = "检查结果")
|
||||||
private Integer result;
|
private Integer result;
|
||||||
|
|
||||||
@JSONField(name = "em")
|
|
||||||
@Schema(description = "错误信息")
|
@Schema(description = "错误信息")
|
||||||
private String errorMessage;
|
private String errorMessage;
|
||||||
|
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主机输出响应 实体对象
|
||||||
|
*
|
||||||
|
* @author Jiahang Li
|
||||||
|
* @version 1.0.0
|
||||||
|
* @since 2023/12/29 16:20
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@SuperBuilder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@Schema(name = "TerminalOutputResponse", description = "主机输出响应 实体对象")
|
||||||
|
public class TerminalOutputResponse extends TerminalBasePayload {
|
||||||
|
|
||||||
|
@Schema(description = "body")
|
||||||
|
private String body;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
package com.orion.ops.module.asset.handler.host.terminal.session;
|
package com.orion.ops.module.asset.handler.host.terminal.session;
|
||||||
|
|
||||||
import com.alibaba.fastjson.JSON;
|
|
||||||
import com.orion.lang.utils.awt.Clipboards;
|
|
||||||
import com.orion.lang.utils.io.Streams;
|
import com.orion.lang.utils.io.Streams;
|
||||||
import com.orion.net.host.SessionStore;
|
import com.orion.net.host.SessionStore;
|
||||||
import com.orion.net.host.ssh.TerminalType;
|
import com.orion.net.host.ssh.TerminalType;
|
||||||
@@ -9,8 +7,9 @@ import com.orion.net.host.ssh.shell.ShellExecutor;
|
|||||||
import com.orion.ops.framework.common.constant.Const;
|
import com.orion.ops.framework.common.constant.Const;
|
||||||
import com.orion.ops.framework.websocket.core.utils.WebSockets;
|
import com.orion.ops.framework.websocket.core.utils.WebSockets;
|
||||||
import com.orion.ops.module.asset.define.AssetThreadPools;
|
import com.orion.ops.module.asset.define.AssetThreadPools;
|
||||||
import com.orion.ops.module.asset.handler.host.terminal.entity.Message;
|
import com.orion.ops.module.asset.handler.host.terminal.enums.OutputTypeEnum;
|
||||||
import com.orion.ops.module.asset.handler.host.terminal.enums.OutputOperatorTypeEnum;
|
import com.orion.ops.module.asset.handler.host.terminal.model.TerminalConfig;
|
||||||
|
import com.orion.ops.module.asset.handler.host.terminal.model.response.TerminalOutputResponse;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.web.socket.WebSocketSession;
|
import org.springframework.web.socket.WebSocketSession;
|
||||||
@@ -18,7 +17,6 @@ import org.springframework.web.socket.WebSocketSession;
|
|||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 终端会话
|
* 终端会话
|
||||||
@@ -36,22 +34,32 @@ public class TerminalSession implements ITerminalSession {
|
|||||||
@Getter
|
@Getter
|
||||||
private final WebSocketSession session;
|
private final WebSocketSession session;
|
||||||
|
|
||||||
|
private final TerminalConfig config;
|
||||||
|
|
||||||
private final SessionStore sessionStore;
|
private final SessionStore sessionStore;
|
||||||
|
|
||||||
private ShellExecutor executor;
|
private ShellExecutor executor;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private String lastLine;
|
||||||
|
|
||||||
private volatile boolean close;
|
private volatile boolean close;
|
||||||
|
|
||||||
public TerminalSession(String token,
|
public TerminalSession(String token,
|
||||||
WebSocketSession session,
|
WebSocketSession session,
|
||||||
SessionStore sessionStore) {
|
SessionStore sessionStore,
|
||||||
|
TerminalConfig config) {
|
||||||
this.token = token;
|
this.token = token;
|
||||||
this.session = session;
|
this.session = session;
|
||||||
this.sessionStore = sessionStore;
|
this.sessionStore = sessionStore;
|
||||||
|
this.config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void connect(int cols, int rows) {
|
public void connect(int cols, int rows) {
|
||||||
|
config.setCols(cols);
|
||||||
|
config.setRows(rows);
|
||||||
|
// 打开 shell
|
||||||
this.executor = sessionStore.getShellExecutor();
|
this.executor = sessionStore.getShellExecutor();
|
||||||
executor.terminalType(TerminalType.XTERM_256_COLOR);
|
executor.terminalType(TerminalType.XTERM_256_COLOR);
|
||||||
executor.size(cols, rows);
|
executor.size(cols, rows);
|
||||||
@@ -66,6 +74,8 @@ public class TerminalSession implements ITerminalSession {
|
|||||||
if (!executor.isConnected()) {
|
if (!executor.isConnected()) {
|
||||||
executor.connect();
|
executor.connect();
|
||||||
}
|
}
|
||||||
|
config.setCols(cols);
|
||||||
|
config.setRows(rows);
|
||||||
executor.size(cols, rows);
|
executor.size(cols, rows);
|
||||||
executor.resize();
|
executor.resize();
|
||||||
}
|
}
|
||||||
@@ -90,7 +100,7 @@ public class TerminalSession implements ITerminalSession {
|
|||||||
Streams.close(executor);
|
Streams.close(executor);
|
||||||
Streams.close(sessionStore);
|
Streams.close(sessionStore);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("terminal 断开连接 失败 token: {}, {}", token, e);
|
log.error("terminal 断开连接 失败 token: {}", token, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,16 +115,14 @@ public class TerminalSession implements ITerminalSession {
|
|||||||
int read;
|
int read;
|
||||||
try {
|
try {
|
||||||
while (session.isOpen() && (read = in.read(bs)) != -1) {
|
while (session.isOpen() && (read = in.read(bs)) != -1) {
|
||||||
|
String body = lastLine = new String(bs, 0, read, config.getCharset());
|
||||||
// 响应
|
// 响应
|
||||||
String body = new String(bs, 0, read, StandardCharsets.UTF_8);
|
TerminalOutputResponse resp = TerminalOutputResponse.builder()
|
||||||
// TODO lastline
|
|
||||||
Message<?> msg = Message.builder()
|
|
||||||
.session(token)
|
.session(token)
|
||||||
.type(OutputOperatorTypeEnum.OUTPUT.getType())
|
.type(OutputTypeEnum.OUTPUT.getType())
|
||||||
// FIXME TERMINAL charset
|
|
||||||
.body(body)
|
.body(body)
|
||||||
.build();
|
.build();
|
||||||
WebSockets.sendJson(session, msg);
|
WebSockets.sendText(session, OutputTypeEnum.OUTPUT.format(resp));
|
||||||
}
|
}
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
log.error("terminal 读取流失败", ex);
|
log.error("terminal 读取流失败", ex);
|
||||||
|
|||||||
Reference in New Issue
Block a user