增加ssh链接框架
This commit is contained in:
parent
80c40bb6ab
commit
24f62c5719
@ -3,30 +3,36 @@ package com.qqchen.deploy.backend.framework.enums;
|
|||||||
import com.fasterxml.jackson.annotation.JsonValue;
|
import com.fasterxml.jackson.annotation.JsonValue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SSH WebSocket消息类型枚举(Framework层)
|
* SSH WebSocket 消息类型枚举(Framework层)
|
||||||
*
|
*
|
||||||
* 用于标识不同类型的SSH WebSocket消息
|
* 定义标准的SSH WebSocket消息类型
|
||||||
|
* 使用 @JsonValue 注解实现JSON序列化为小写字符串
|
||||||
*/
|
*/
|
||||||
public enum SSHMessageType {
|
public enum SSHMessageType {
|
||||||
/**
|
/**
|
||||||
* 用户输入(前端 → 后端)
|
* 用户输入消息(前端 → 后端)
|
||||||
*/
|
*/
|
||||||
INPUT("input"),
|
INPUT("input"),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 终端输出(后端 → 前端)
|
* SSH输出消息(后端 → 前端)
|
||||||
*/
|
*/
|
||||||
OUTPUT("output"),
|
OUTPUT("output"),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 连接状态(后端 → 前端)
|
* 连接状态消息(后端 → 前端)
|
||||||
*/
|
*/
|
||||||
STATUS("status"),
|
STATUS("status"),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 错误信息(后端 → 前端)
|
* 错误消息(后端 → 前端)
|
||||||
*/
|
*/
|
||||||
ERROR("error");
|
ERROR("error"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 终端尺寸调整消息(前端 → 后端)
|
||||||
|
*/
|
||||||
|
RESIZE("resize");
|
||||||
|
|
||||||
private final String value;
|
private final String value;
|
||||||
|
|
||||||
|
|||||||
@ -310,28 +310,84 @@ public abstract class AbstractSSHWebSocketHandler extends TextWebSocketHandler {
|
|||||||
SSHWebSocketMessage msg = JsonUtils.fromJson(message.getPayload(), SSHWebSocketMessage.class);
|
SSHWebSocketMessage msg = JsonUtils.fromJson(message.getPayload(), SSHWebSocketMessage.class);
|
||||||
|
|
||||||
if (msg.getType() == SSHMessageType.INPUT) {
|
if (msg.getType() == SSHMessageType.INPUT) {
|
||||||
// 用户输入命令
|
// 用户输入命令(使用强类型Request)
|
||||||
|
com.qqchen.deploy.backend.framework.ssh.websocket.request.SSHInputRequest request =
|
||||||
|
msg.getRequest(com.qqchen.deploy.backend.framework.ssh.websocket.request.SSHInputRequest.class);
|
||||||
|
|
||||||
|
if (request == null || !request.isValid()) {
|
||||||
|
log.warn("INPUT消息格式错误或为空: sessionId={}", sessionId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Session.Shell shell = sshShells.get(sessionId);
|
Session.Shell shell = sshShells.get(sessionId);
|
||||||
if (shell != null) {
|
if (shell != null) {
|
||||||
OutputStream outputStream = shell.getOutputStream();
|
OutputStream outputStream = shell.getOutputStream();
|
||||||
outputStream.write(msg.getData().getBytes(StandardCharsets.UTF_8));
|
outputStream.write(request.getCommand().getBytes(StandardCharsets.UTF_8));
|
||||||
outputStream.flush();
|
outputStream.flush();
|
||||||
|
|
||||||
// 触发命令事件
|
// 触发命令事件
|
||||||
SSHTarget target = sessionTargets.get(sessionId);
|
SSHTarget target = sessionTargets.get(sessionId);
|
||||||
SSHEventData eventData = SSHEventData.builder()
|
SSHEventData eventData = SSHEventData.builder()
|
||||||
.sessionId(sessionId)
|
.sessionId(sessionId)
|
||||||
.command(msg.getData())
|
.command(request.getCommand())
|
||||||
.target(target)
|
.target(target)
|
||||||
.build();
|
.build();
|
||||||
onEvent(SSHEvent.ON_COMMAND, eventData);
|
onEvent(SSHEvent.ON_COMMAND, eventData);
|
||||||
}
|
}
|
||||||
|
} else if (msg.getType() == SSHMessageType.RESIZE) {
|
||||||
|
// 终端尺寸调整
|
||||||
|
handleResizeMessage(sessionId, msg);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("处理WebSocket消息失败: sessionId={}", sessionId, e);
|
log.error("处理WebSocket消息失败: sessionId={}", sessionId, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理终端尺寸调整消息(使用强类型Request)
|
||||||
|
*
|
||||||
|
* @param sessionId 会话ID
|
||||||
|
* @param msg 消息对象
|
||||||
|
*/
|
||||||
|
private void handleResizeMessage(String sessionId, SSHWebSocketMessage msg) {
|
||||||
|
try {
|
||||||
|
// 使用强类型提取request
|
||||||
|
com.qqchen.deploy.backend.framework.ssh.websocket.request.SSHResizeRequest request =
|
||||||
|
msg.getRequest(com.qqchen.deploy.backend.framework.ssh.websocket.request.SSHResizeRequest.class);
|
||||||
|
|
||||||
|
if (request == null) {
|
||||||
|
log.warn("RESIZE消息缺少request或格式错误: sessionId={}", sessionId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证参数
|
||||||
|
if (!request.isValid()) {
|
||||||
|
log.warn("RESIZE消息参数无效: sessionId={}, rows={}, cols={}",
|
||||||
|
sessionId, request.getRows(), request.getCols());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调整SSH PTY尺寸
|
||||||
|
Session.Shell shell = sshShells.get(sessionId);
|
||||||
|
if (shell != null) {
|
||||||
|
// 计算像素尺寸(标准字体:8x16像素)
|
||||||
|
int widthPixels = request.getCols() * 8;
|
||||||
|
int heightPixels = request.getRows() * 16;
|
||||||
|
|
||||||
|
shell.changeWindowDimensions(request.getCols(), request.getRows(),
|
||||||
|
widthPixels, heightPixels);
|
||||||
|
|
||||||
|
log.debug("SSH终端尺寸已调整: sessionId={}, cols={}, rows={}",
|
||||||
|
sessionId, request.getCols(), request.getRows());
|
||||||
|
} else {
|
||||||
|
log.warn("未找到SSH Shell,无法调整尺寸: sessionId={}", sessionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("处理RESIZE消息失败: sessionId={}", sessionId, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
|
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
|
||||||
String sessionId = getSessionId(session);
|
String sessionId = getSessionId(session);
|
||||||
@ -624,7 +680,7 @@ public abstract class AbstractSSHWebSocketHandler extends TextWebSocketHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 发送输出消息到前端
|
* 发送输出消息到前端(使用强类型Response)
|
||||||
*
|
*
|
||||||
* @param session WebSocket会话
|
* @param session WebSocket会话
|
||||||
* @param output 输出内容
|
* @param output 输出内容
|
||||||
@ -640,8 +696,16 @@ public abstract class AbstractSSHWebSocketHandler extends TextWebSocketHandler {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
log.debug(" ├─ 创建SSHWebSocketMessage: sessionId={}", sessionId);
|
// 创建强类型Response对象
|
||||||
SSHWebSocketMessage msg = SSHWebSocketMessage.output(output);
|
com.qqchen.deploy.backend.framework.ssh.websocket.response.SSHOutputResponse response =
|
||||||
|
new com.qqchen.deploy.backend.framework.ssh.websocket.response.SSHOutputResponse(output);
|
||||||
|
|
||||||
|
// 包装成 {"response": SSHOutputResponse}
|
||||||
|
Map<String, Object> data = new HashMap<>();
|
||||||
|
data.put("response", response);
|
||||||
|
|
||||||
|
// 创建消息
|
||||||
|
SSHWebSocketMessage msg = new SSHWebSocketMessage(SSHMessageType.OUTPUT, data);
|
||||||
|
|
||||||
log.debug(" ├─ 准备调用session.sendMessage: sessionId={}", sessionId);
|
log.debug(" ├─ 准备调用session.sendMessage: sessionId={}", sessionId);
|
||||||
session.sendMessage(new TextMessage(JsonUtils.toJson(msg)));
|
session.sendMessage(new TextMessage(JsonUtils.toJson(msg)));
|
||||||
@ -655,11 +719,25 @@ public abstract class AbstractSSHWebSocketHandler extends TextWebSocketHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 发送状态消息到前端
|
* 发送状态消息到前端(使用强类型Response)
|
||||||
*/
|
*/
|
||||||
protected void sendStatus(WebSocketSession session, SSHStatusEnum status) {
|
protected void sendStatus(WebSocketSession session, SSHStatusEnum status) {
|
||||||
try {
|
try {
|
||||||
SSHWebSocketMessage msg = SSHWebSocketMessage.status(status);
|
if (!session.isOpen()) {
|
||||||
|
log.debug("WebSocket已关闭,跳过发送状态: sessionId={}", session.getId());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建强类型Response对象
|
||||||
|
com.qqchen.deploy.backend.framework.ssh.websocket.response.SSHStatusResponse response =
|
||||||
|
new com.qqchen.deploy.backend.framework.ssh.websocket.response.SSHStatusResponse(status);
|
||||||
|
|
||||||
|
// 包装成 {"response": SSHStatusResponse}
|
||||||
|
Map<String, Object> data = new HashMap<>();
|
||||||
|
data.put("response", response);
|
||||||
|
|
||||||
|
// 创建消息
|
||||||
|
SSHWebSocketMessage msg = new SSHWebSocketMessage(SSHMessageType.STATUS, data);
|
||||||
session.sendMessage(new TextMessage(JsonUtils.toJson(msg)));
|
session.sendMessage(new TextMessage(JsonUtils.toJson(msg)));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log.error("发送状态消息失败: sessionId={}", session.getId(), e);
|
log.error("发送状态消息失败: sessionId={}", session.getId(), e);
|
||||||
@ -667,7 +745,7 @@ public abstract class AbstractSSHWebSocketHandler extends TextWebSocketHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 发送错误消息到前端
|
* 发送错误消息到前端(使用强类型Response)
|
||||||
*/
|
*/
|
||||||
protected void sendError(WebSocketSession session, String error) {
|
protected void sendError(WebSocketSession session, String error) {
|
||||||
try {
|
try {
|
||||||
@ -675,7 +753,17 @@ public abstract class AbstractSSHWebSocketHandler extends TextWebSocketHandler {
|
|||||||
log.debug("WebSocket已关闭,跳过发送错误消息: sessionId={}", session.getId());
|
log.debug("WebSocket已关闭,跳过发送错误消息: sessionId={}", session.getId());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
SSHWebSocketMessage msg = SSHWebSocketMessage.error(error);
|
|
||||||
|
// 创建强类型Response对象
|
||||||
|
com.qqchen.deploy.backend.framework.ssh.websocket.response.SSHErrorResponse response =
|
||||||
|
new com.qqchen.deploy.backend.framework.ssh.websocket.response.SSHErrorResponse(error);
|
||||||
|
|
||||||
|
// 包装成 {"response": SSHErrorResponse}
|
||||||
|
Map<String, Object> data = new HashMap<>();
|
||||||
|
data.put("response", response);
|
||||||
|
|
||||||
|
// 创建消息
|
||||||
|
SSHWebSocketMessage msg = new SSHWebSocketMessage(SSHMessageType.ERROR, data);
|
||||||
session.sendMessage(new TextMessage(JsonUtils.toJson(msg)));
|
session.sendMessage(new TextMessage(JsonUtils.toJson(msg)));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
if (session.isOpen()) {
|
if (session.isOpen()) {
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package com.qqchen.deploy.backend.framework.ssh.websocket;
|
package com.qqchen.deploy.backend.framework.ssh.websocket;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.qqchen.deploy.backend.framework.enums.SSHMessageType;
|
import com.qqchen.deploy.backend.framework.enums.SSHMessageType;
|
||||||
import com.qqchen.deploy.backend.framework.enums.SSHStatusEnum;
|
import com.qqchen.deploy.backend.framework.enums.SSHStatusEnum;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
@ -30,13 +31,23 @@ public class SSHWebSocketMessage {
|
|||||||
private SSHMessageType type;
|
private SSHMessageType type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 消息内容(字符串)
|
* 消息内容(可以是字符串或对象)
|
||||||
|
*
|
||||||
|
* 字符串类型(后端→前端):
|
||||||
* - type=OUTPUT: 终端输出内容
|
* - type=OUTPUT: 终端输出内容
|
||||||
* - type=INPUT: 用户输入内容
|
|
||||||
* - type=STATUS: 状态值(connecting/connected/reconnecting/disconnected/error)
|
* - type=STATUS: 状态值(connecting/connected/reconnecting/disconnected/error)
|
||||||
* - type=ERROR: 错误描述信息
|
* - type=ERROR: 错误描述信息
|
||||||
|
*
|
||||||
|
* 字符串类型(前端→后端):
|
||||||
|
* - type=INPUT: 用户输入内容
|
||||||
|
*
|
||||||
|
* 对象类型(前端→后端):
|
||||||
|
* - type=RESIZE: {"request": {"rows": 40, "cols": 150}}
|
||||||
|
*
|
||||||
|
* 对象类型(后端→前端,预留):
|
||||||
|
* - 未来可能的response: {"response": {...}}
|
||||||
*/
|
*/
|
||||||
private String data;
|
private Object data;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 消息时间戳(Unix毫秒)
|
* 消息时间戳(Unix毫秒)
|
||||||
@ -46,14 +57,15 @@ public class SSHWebSocketMessage {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 可选元数据
|
* 可选元数据
|
||||||
* 通常为null,特殊场景下使用
|
*
|
||||||
|
* 用于特殊场景的扩展字段,通常为null
|
||||||
*/
|
*/
|
||||||
private Map<String, Object> metadata;
|
private Map<String, Object> metadata;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构造函数(自动填充时间戳)
|
* 构造函数(自动填充时间戳)
|
||||||
*/
|
*/
|
||||||
public SSHWebSocketMessage(SSHMessageType type, String data) {
|
public SSHWebSocketMessage(SSHMessageType type, Object data) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.data = data;
|
this.data = data;
|
||||||
this.timestamp = System.currentTimeMillis();
|
this.timestamp = System.currentTimeMillis();
|
||||||
@ -94,4 +106,40 @@ public class SSHWebSocketMessage {
|
|||||||
public static SSHWebSocketMessage input(String data) {
|
public static SSHWebSocketMessage input(String data) {
|
||||||
return new SSHWebSocketMessage(SSHMessageType.INPUT, data);
|
return new SSHWebSocketMessage(SSHMessageType.INPUT, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从消息中提取request对象(强类型转换)
|
||||||
|
*
|
||||||
|
* @param requestClass 请求类型
|
||||||
|
* @return 请求对象,如果解析失败返回null
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T> T getRequest(Class<T> requestClass) {
|
||||||
|
try {
|
||||||
|
if (data instanceof Map) {
|
||||||
|
Map<String, Object> dataMap = (Map<String, Object>) data;
|
||||||
|
Object requestObj = dataMap.get("request");
|
||||||
|
|
||||||
|
if (requestObj != null) {
|
||||||
|
// 如果已经是目标类型,直接返回
|
||||||
|
if (requestClass.isInstance(requestObj)) {
|
||||||
|
return (T) requestObj;
|
||||||
|
}
|
||||||
|
// 否则使用ObjectMapper转换(Map -> POJO)
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
return mapper.convertValue(requestObj, requestClass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 转换失败,返回null
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查data是否包含request
|
||||||
|
*/
|
||||||
|
public boolean hasRequest() {
|
||||||
|
return data instanceof Map && ((Map<?, ?>) data).containsKey("request");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,29 @@
|
|||||||
|
package com.qqchen.deploy.backend.framework.ssh.websocket.request;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonSubTypes;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||||
|
import com.qqchen.deploy.backend.framework.enums.SSHMessageType;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SSH WebSocket请求基类(Framework层)
|
||||||
|
*
|
||||||
|
* 所有前端发送到后端的请求消息都应该继承此类
|
||||||
|
* 使用Jackson的@JsonTypeInfo和@JsonSubTypes实现多态反序列化
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@JsonTypeInfo(
|
||||||
|
use = JsonTypeInfo.Id.NAME,
|
||||||
|
property = "type",
|
||||||
|
visible = true
|
||||||
|
)
|
||||||
|
@JsonSubTypes({
|
||||||
|
@JsonSubTypes.Type(value = SSHInputRequest.class, name = "input"),
|
||||||
|
@JsonSubTypes.Type(value = SSHResizeRequest.class, name = "resize")
|
||||||
|
})
|
||||||
|
public abstract class SSHBaseRequest {
|
||||||
|
/**
|
||||||
|
* 请求类型(使用枚举保证类型安全)
|
||||||
|
*/
|
||||||
|
private SSHMessageType type;
|
||||||
|
}
|
||||||
@ -0,0 +1,45 @@
|
|||||||
|
package com.qqchen.deploy.backend.framework.ssh.websocket.request;
|
||||||
|
|
||||||
|
import com.qqchen.deploy.backend.framework.enums.SSHMessageType;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SSH用户输入请求(Framework层)
|
||||||
|
*
|
||||||
|
* 前端发送格式:
|
||||||
|
* {
|
||||||
|
* "type": "input",
|
||||||
|
* "data": {
|
||||||
|
* "request": {
|
||||||
|
* "type": "input",
|
||||||
|
* "command": "ls -la"
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class SSHInputRequest extends SSHBaseRequest {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户输入命令(终端命令或字符)
|
||||||
|
*/
|
||||||
|
private String command;
|
||||||
|
|
||||||
|
public SSHInputRequest() {
|
||||||
|
setType(SSHMessageType.INPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SSHInputRequest(String command) {
|
||||||
|
this();
|
||||||
|
this.command = command;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证参数有效性
|
||||||
|
*/
|
||||||
|
public boolean isValid() {
|
||||||
|
return command != null && !command.isEmpty();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,45 @@
|
|||||||
|
package com.qqchen.deploy.backend.framework.ssh.websocket.request;
|
||||||
|
|
||||||
|
import com.qqchen.deploy.backend.framework.enums.SSHMessageType;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SSH终端尺寸调整请求(Framework层)
|
||||||
|
*
|
||||||
|
* 前端发送格式:
|
||||||
|
* {
|
||||||
|
* "type": "resize",
|
||||||
|
* "data": {
|
||||||
|
* "request": {
|
||||||
|
* "rows": 40,
|
||||||
|
* "cols": 150
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class SSHResizeRequest extends SSHBaseRequest {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 终端行数
|
||||||
|
*/
|
||||||
|
private Integer rows;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 终端列数
|
||||||
|
*/
|
||||||
|
private Integer cols;
|
||||||
|
|
||||||
|
public SSHResizeRequest() {
|
||||||
|
setType(SSHMessageType.RESIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证参数有效性
|
||||||
|
*/
|
||||||
|
public boolean isValid() {
|
||||||
|
return rows != null && cols != null && rows > 0 && cols > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
package com.qqchen.deploy.backend.framework.ssh.websocket.response;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonSubTypes;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||||
|
import com.qqchen.deploy.backend.framework.enums.SSHMessageType;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SSH WebSocket响应基类(Framework层)
|
||||||
|
*
|
||||||
|
* 所有后端发送到前端的响应消息都应该继承此类
|
||||||
|
* 使用Jackson的@JsonTypeInfo和@JsonSubTypes实现多态序列化
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@JsonTypeInfo(
|
||||||
|
use = JsonTypeInfo.Id.NAME,
|
||||||
|
property = "type",
|
||||||
|
visible = true
|
||||||
|
)
|
||||||
|
@JsonSubTypes({
|
||||||
|
@JsonSubTypes.Type(value = SSHOutputResponse.class, name = "output"),
|
||||||
|
@JsonSubTypes.Type(value = SSHStatusResponse.class, name = "status"),
|
||||||
|
@JsonSubTypes.Type(value = SSHErrorResponse.class, name = "error")
|
||||||
|
})
|
||||||
|
public abstract class SSHBaseResponse {
|
||||||
|
/**
|
||||||
|
* 响应类型(使用枚举保证类型安全)
|
||||||
|
*/
|
||||||
|
private SSHMessageType type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 响应数据(统一字段)
|
||||||
|
* - OUTPUT: 终端输出内容
|
||||||
|
* - STATUS: 状态值(connected/disconnected等)
|
||||||
|
* - ERROR: 错误消息
|
||||||
|
*/
|
||||||
|
private String data;
|
||||||
|
}
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
package com.qqchen.deploy.backend.framework.ssh.websocket.response;
|
||||||
|
|
||||||
|
import com.qqchen.deploy.backend.framework.enums.SSHMessageType;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SSH错误响应(Framework层)
|
||||||
|
*
|
||||||
|
* 后端发送格式:
|
||||||
|
* {
|
||||||
|
* "type": "error",
|
||||||
|
* "data": {
|
||||||
|
* "response": {
|
||||||
|
* "type": "error",
|
||||||
|
* "data": "Connection failed"
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class SSHErrorResponse extends SSHBaseResponse {
|
||||||
|
|
||||||
|
public SSHErrorResponse() {
|
||||||
|
setType(SSHMessageType.ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SSHErrorResponse(String message) {
|
||||||
|
this();
|
||||||
|
setType(SSHMessageType.ERROR);
|
||||||
|
setData(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
package com.qqchen.deploy.backend.framework.ssh.websocket.response;
|
||||||
|
|
||||||
|
import com.qqchen.deploy.backend.framework.enums.SSHMessageType;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SSH终端输出响应(Framework层)
|
||||||
|
*
|
||||||
|
* 后端发送格式:
|
||||||
|
* {
|
||||||
|
* "type": "output",
|
||||||
|
* "data": {
|
||||||
|
* "response": {
|
||||||
|
* "type": "output",
|
||||||
|
* "data": "terminal output text"
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class SSHOutputResponse extends SSHBaseResponse {
|
||||||
|
|
||||||
|
public SSHOutputResponse() {
|
||||||
|
setType(SSHMessageType.OUTPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SSHOutputResponse(String data) {
|
||||||
|
this();
|
||||||
|
setType(SSHMessageType.OUTPUT);
|
||||||
|
setData(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,41 @@
|
|||||||
|
package com.qqchen.deploy.backend.framework.ssh.websocket.response;
|
||||||
|
|
||||||
|
import com.qqchen.deploy.backend.framework.enums.SSHMessageType;
|
||||||
|
import com.qqchen.deploy.backend.framework.enums.SSHStatusEnum;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SSH连接状态响应(Framework层)
|
||||||
|
*
|
||||||
|
* 后端发送格式:
|
||||||
|
* {
|
||||||
|
* "type": "status",
|
||||||
|
* "data": {
|
||||||
|
* "response": {
|
||||||
|
* "type": "status",
|
||||||
|
* "data": "connected"
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class SSHStatusResponse extends SSHBaseResponse {
|
||||||
|
|
||||||
|
public SSHStatusResponse() {
|
||||||
|
setType(SSHMessageType.STATUS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SSHStatusResponse(SSHStatusEnum status) {
|
||||||
|
this();
|
||||||
|
setType(SSHMessageType.STATUS);
|
||||||
|
setData(status.name().toLowerCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
public SSHStatusResponse(String status) {
|
||||||
|
this();
|
||||||
|
setType(SSHMessageType.STATUS);
|
||||||
|
setData(status);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user