WebSocket集群处理。

This commit is contained in:
mxd
2021-07-14 23:27:48 +08:00
parent ea77ab9c12
commit e417b5bef2
12 changed files with 378 additions and 166 deletions

View File

@@ -1,5 +1,7 @@
package org.ssssssss.magicapi.spring.boot.starter;
import java.util.UUID;
/**
* 集群配置
* @since 1.2.0
@@ -14,7 +16,7 @@ public class ClusterConfig {
/**
* 实例ID集群环境下要保证每台机器不同。默认启动后随机生成uuid
*/
private String instanceId;
private String instanceId = UUID.randomUUID().toString();
/**
* redis 通道

View File

@@ -120,6 +120,8 @@ public class MagicAPIAutoConfiguration implements WebMvcConfigurer, WebSocketCon
*/
private final ObjectProvider<List<MagicFunction>> magicFunctionsProvider;
private final ObjectProvider<MagicNotifyService> magicNotifyServiceProvider;
private final Environment environment;
private final MagicCorsFilter magicCorsFilter = new MagicCorsFilter();
@@ -145,6 +147,7 @@ public class MagicAPIAutoConfiguration implements WebMvcConfigurer, WebSocketCon
ObjectProvider<List<ColumnMapperProvider>> columnMapperProvidersProvider,
ObjectProvider<List<MagicFunction>> magicFunctionsProvider,
ObjectProvider<RestTemplate> restTemplateProvider,
ObjectProvider<MagicNotifyService> magicNotifyServiceProvider,
ObjectProvider<AuthorizationInterceptor> authorizationInterceptorProvider,
Environment environment,
ApplicationContext applicationContext
@@ -158,6 +161,7 @@ public class MagicAPIAutoConfiguration implements WebMvcConfigurer, WebSocketCon
this.columnMapperProvidersProvider = columnMapperProvidersProvider;
this.magicFunctionsProvider = magicFunctionsProvider;
this.restTemplateProvider = restTemplateProvider;
this.magicNotifyServiceProvider = magicNotifyServiceProvider;
this.authorizationInterceptorProvider = authorizationInterceptorProvider;
this.environment = environment;
this.applicationContext = applicationContext;
@@ -352,9 +356,8 @@ public class MagicAPIAutoConfiguration implements WebMvcConfigurer, WebSocketCon
ResultProvider resultProvider,
MagicDynamicDataSource magicDynamicDataSource,
MagicFunctionManager magicFunctionManager,
MagicNotifyService magicNotifyService,
Resource workspace) {
return new DefaultMagicAPIService(mappingHandlerMapping, apiServiceProvider, functionServiceProvider, groupServiceProvider, resultProvider, magicDynamicDataSource, magicFunctionManager, magicNotifyService, properties.getClusterConfig().getInstanceId(), workspace, properties.isThrowException());
return new DefaultMagicAPIService(mappingHandlerMapping, apiServiceProvider, functionServiceProvider, groupServiceProvider, resultProvider, magicDynamicDataSource, magicFunctionManager, magicNotifyServiceProvider.getObject(), properties.getClusterConfig().getInstanceId(), workspace, properties.isThrowException());
}
private void setupSpringSecurity() {
@@ -584,9 +587,10 @@ public class MagicAPIAutoConfiguration implements WebMvcConfigurer, WebSocketCon
public void registerWebSocketHandlers(WebSocketHandlerRegistry webSocketHandlerRegistry) {
String web = properties.getWeb();
if (web != null) {
WebSocketHandlerRegistration registration = webSocketHandlerRegistry.addHandler(new MagicWebSocketDispatcher(Arrays.asList(
MagicWebSocketDispatcher dispatcher = new MagicWebSocketDispatcher(properties.getClusterConfig().getInstanceId(),magicNotifyServiceProvider.getObject(), Arrays.asList(
new MagicDebugHandler()
)), web + "/console");
));
WebSocketHandlerRegistration registration = webSocketHandlerRegistry.addHandler(dispatcher, web + "/console");
if (properties.isSupportCrossDomain()) {
registration.setAllowedOrigins("*");
}

View File

@@ -3,9 +3,12 @@ package org.ssssssss.magicapi.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.ssssssss.magicapi.model.Constants;
import org.ssssssss.magicapi.model.MagicConsoleSession;
import org.ssssssss.magicapi.model.MagicNotify;
import org.ssssssss.magicapi.provider.MagicNotifyService;
import org.ssssssss.magicapi.utils.JsonUtils;
import org.ssssssss.script.MagicScriptDebugContext;
import java.io.IOException;
import java.util.Map;
@@ -13,60 +16,84 @@ import java.util.concurrent.ConcurrentHashMap;
public class WebSocketSessionManager {
private static Logger logger = LoggerFactory.getLogger(WebSocketSessionManager.class);
private static final Logger logger = LoggerFactory.getLogger(WebSocketSessionManager.class);
private static final Map<String, WebSocketSession> SESSION = new ConcurrentHashMap<>();
private static final Map<String, MagicConsoleSession> SESSION = new ConcurrentHashMap<>();
public static void add(WebSocketSession session) {
private static MagicNotifyService magicNotifyService;
private static String instanceId;
public static void add(MagicConsoleSession session) {
SESSION.put(session.getId(), session);
}
public static void remove(WebSocketSession session) {
SESSION.remove(session.getId());
public static void remove(MagicConsoleSession session) {
if(session.getId() != null){
remove(session.getId());
}
}
public static void remove(String sessionId) {
SESSION.remove(sessionId);
}
public static void sendBySessionId(String sessionId, MessageType messageType, Object... values) {
WebSocketSession session = findSession(sessionId);
if (session != null) {
StringBuilder builder = new StringBuilder(messageType.name().toLowerCase());
if (values != null) {
for (int i = 0, len = values.length; i < len; i++) {
builder.append(",");
Object value = values[i];
if (i + 1 < len || value instanceof CharSequence || value instanceof Number) {
builder.append(value);
} else {
builder.append(JsonUtils.toJsonString(value));
}
MagicConsoleSession session = findSession(sessionId);
StringBuilder builder = new StringBuilder(messageType.name().toLowerCase());
if (values != null) {
for (int i = 0, len = values.length; i < len; i++) {
builder.append(",");
Object value = values[i];
if (i + 1 < len || value instanceof CharSequence || value instanceof Number) {
builder.append(value);
} else {
builder.append(JsonUtils.toJsonString(value));
}
}
try {
session.sendMessage(new TextMessage(builder.toString()));
} catch (IOException e) {
logger.error("发送WebSocket消息失败", e);
}
}
if (session != null && session.writeable()) {
sendBySession(session, builder.toString());
} else if(magicNotifyService != null){
// 通知其他机器去发送消息
magicNotifyService.sendNotify(new MagicNotify(instanceId, Constants.NOTIFY_WS_S_C, sessionId, builder.toString()));
}
}
/**
* 获取Session中的属性
*/
public static <T> T getSessionAttribute(String sessionId, String key) {
WebSocketSession session = findSession(sessionId);
public static void sendBySessionId(String sessionId, String content){
MagicConsoleSession session = findSession(sessionId);
if (session != null) {
return (T) session.getAttributes().get(key);
}
return null;
}
public static void setSessionAttribute(String sessionId, String key, Object value) {
WebSocketSession session = findSession(sessionId);
if (session != null) {
session.getAttributes().put(key, value);
sendBySession(session, content);
}
}
private static WebSocketSession findSession(String sessionId) {
return SESSION.values().stream().filter(it -> sessionId.equals(it.getAttributes().get(Constants.WS_DEBUG_SESSION_KEY))).findFirst().orElse(null);
public static void sendBySession(MagicConsoleSession session, String content){
try {
session.getWebSocketSession().sendMessage(new TextMessage(content));
} catch (IOException e) {
logger.error("发送WebSocket消息失败", e);
}
}
public static MagicConsoleSession findSession(String sessionId) {
return SESSION.get(sessionId);
}
public static void setMagicNotifyService(MagicNotifyService magicNotifyService) {
WebSocketSessionManager.magicNotifyService = magicNotifyService;
}
public static void setInstanceId(String instanceId) {
WebSocketSessionManager.instanceId = instanceId;
}
public static void createSession(String sessionId, MagicScriptDebugContext debugContext){
MagicConsoleSession consoleSession = SESSION.get(sessionId);
if(consoleSession == null){
consoleSession = new MagicConsoleSession(sessionId, debugContext);
SESSION.put(sessionId, consoleSession);
}else{
consoleSession.setMagicScriptDebugContext(debugContext);
}
}
}

View File

@@ -1,47 +1,57 @@
package org.ssssssss.magicapi.controller;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.socket.WebSocketSession;
import org.ssssssss.magicapi.config.Message;
import org.ssssssss.magicapi.config.MessageType;
import org.ssssssss.magicapi.model.Constants;
import org.ssssssss.magicapi.config.WebSocketSessionManager;
import org.ssssssss.magicapi.model.MagicConsoleSession;
import org.ssssssss.script.MagicScriptDebugContext;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class MagicDebugHandler {
/**
* 设置会话ID
* 只在本机处理。
*/
@Message(MessageType.SET_SESSION_ID)
public void setSessionId(WebSocketSession session, String sessionId) {
if(StringUtils.isNotBlank(sessionId)){
session.getAttributes().put(Constants.WS_DEBUG_SESSION_KEY, sessionId);
}
public void setSessionId(MagicConsoleSession session, String sessionId) {
WebSocketSessionManager.remove(session);
session.setId(sessionId);
WebSocketSessionManager.add(session);
}
/**
* 设置断点
* 当本机没有该Session时通知其他机器处理
*/
@Message(MessageType.SET_BREAKPOINT)
public void setBreakPoint(WebSocketSession session, String breakpoints) {
if(StringUtils.isNotBlank(breakpoints)){
MagicScriptDebugContext context = (MagicScriptDebugContext) session.getAttributes().get(Constants.WS_DEBUG_MAGIC_SCRIPT_CONTEXT);
public boolean setBreakPoint(MagicConsoleSession session, String breakpoints) {
MagicScriptDebugContext context = session.getMagicScriptDebugContext();
if (context != null) {
context.setBreakpoints(Stream.of(breakpoints.split(",")).map(Integer::valueOf).collect(Collectors.toList()));
return true;
}
return false;
}
/**
* 恢复断点
* 当本机没有该Session时通知其他机器处理
*/
@Message(MessageType.RESUME_BREAKPOINT)
public void resumeBreakpoint(WebSocketSession session, String stepInto) {
MagicScriptDebugContext context = (MagicScriptDebugContext) session.getAttributes().get(Constants.WS_DEBUG_MAGIC_SCRIPT_CONTEXT);
context.setStepInto("1".equals(stepInto));
try {
context.singal();
} catch (InterruptedException ignored) {
public boolean resumeBreakpoint(MagicConsoleSession session, String stepInto) {
MagicScriptDebugContext context = session.getMagicScriptDebugContext();
if (context != null) {
context.setStepInto("1".equals(stepInto));
try {
context.singal();
} catch (InterruptedException ignored) {
}
return true;
}
return false;
}
}

View File

@@ -8,6 +8,10 @@ import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import org.ssssssss.magicapi.config.Message;
import org.ssssssss.magicapi.config.WebSocketSessionManager;
import org.ssssssss.magicapi.model.Constants;
import org.ssssssss.magicapi.model.MagicConsoleSession;
import org.ssssssss.magicapi.model.MagicNotify;
import org.ssssssss.magicapi.provider.MagicNotifyService;
import org.ssssssss.magicapi.utils.JsonUtils;
import org.ssssssss.script.reflection.MethodInvoker;
@@ -20,9 +24,17 @@ public class MagicWebSocketDispatcher extends TextWebSocketHandler {
private static final Logger logger = LoggerFactory.getLogger(MagicWebSocketDispatcher.class);
private final Map<String, MethodInvoker> handlers = new HashMap<>();
private static final Map<String, MethodInvoker> handlers = new HashMap<>();
public MagicWebSocketDispatcher(List<Object> websocketMessageHandlers) {
private final String instanceId;
private final MagicNotifyService magicNotifyService;
public MagicWebSocketDispatcher(String instanceId, MagicNotifyService magicNotifyService, List<Object> websocketMessageHandlers) {
this.instanceId = instanceId;
this.magicNotifyService = magicNotifyService;
WebSocketSessionManager.setMagicNotifyService(magicNotifyService);
WebSocketSessionManager.setInstanceId(instanceId);
websocketMessageHandlers.forEach(websocketMessageHandler ->
Stream.of(websocketMessageHandler.getClass().getDeclaredMethods())
.forEach(method -> handlers.put(method.getAnnotation(Message.class).value().name().toLowerCase(), new MethodInvoker(method, websocketMessageHandler)))
@@ -30,51 +42,64 @@ public class MagicWebSocketDispatcher extends TextWebSocketHandler {
}
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
WebSocketSessionManager.add(session);
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
WebSocketSessionManager.remove(session);
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
MagicConsoleSession.remove(session);
}
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) {
MagicConsoleSession consoleSession = MagicConsoleSession.from(session);
Object returnValue = findHandleAndInvoke(consoleSession, message.getPayload());
// 如果未成功处理消息,则通知其他机器去处理消息
if (Boolean.FALSE.equals(returnValue)) {
magicNotifyService.sendNotify(new MagicNotify(instanceId, Constants.NOTIFY_WS_C_S, consoleSession.getId(), message.getPayload()));
}
}
private static Object findHandleAndInvoke(MagicConsoleSession session, String payload) {
// messageType[, data][,data]
String payload = message.getPayload();
int index = payload.indexOf(",");
String msgType = index == -1 ? payload : payload.substring(0, index);
MethodInvoker invoker = handlers.get(msgType);
if (invoker != null) {
Object returnValue;
try {
Class<?>[] pTypes = invoker.getParameterTypes();
int pCount = pTypes.length;
if (pCount == 0) {
invoker.invoke0(null, null);
returnValue = invoker.invoke0(null, null);
} else {
Object[] pValues = new Object[pCount];
for (int i = 0; i < pCount; i++) {
Class<?> pType = pTypes[i];
if (pType == WebSocketSession.class) {
if (pType == MagicConsoleSession.class) {
pValues[i] = session;
} else if (pType == String.class) {
int subIndex = payload.indexOf(",", index + 1);
if (subIndex > -1) {
pValues[i] = payload.substring(index + 1, index = subIndex);
} else if(index > -1){
} else if (index > -1) {
pValues[i] = payload.substring(index + 1);
}
} else {
pValues[i] = JsonUtils.readValue(payload, pType);
}
}
invoker.invoke0(null, null, pValues);
returnValue = invoker.invoke0(null, null, pValues);
}
return returnValue;
} catch (Throwable e) {
logger.error("WebSocket消息处理出错", e);
}
}
return null;
}
public static void processMessageReceived(String sessionId, String payload) {
MagicConsoleSession session = WebSocketSessionManager.findSession(sessionId);
if (session != null) {
findHandleAndInvoke(session, payload);
}
}
}

View File

@@ -11,7 +11,10 @@ import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.ssssssss.magicapi.config.*;
import org.ssssssss.magicapi.config.MagicConfiguration;
import org.ssssssss.magicapi.config.MappingHandlerMapping;
import org.ssssssss.magicapi.config.Valid;
import org.ssssssss.magicapi.config.WebSocketSessionManager;
import org.ssssssss.magicapi.context.CookieContext;
import org.ssssssss.magicapi.context.RequestContext;
import org.ssssssss.magicapi.context.SessionContext;
@@ -113,14 +116,15 @@ public class RequestHandler extends MagicController {
if ((value = doPreHandle(requestEntity)) != null) {
return value;
}
if(requestedFromTest){
if (requestedFromTest) {
try {
MagicLoggerContext.SESSION.set(sessionId);
return invokeRequest(requestEntity);
} finally {
MagicLoggerContext.SESSION.remove();
WebSocketSessionManager.remove(sessionId);
}
}else{
} else {
return invokeRequest(requestEntity);
}
}
@@ -272,7 +276,7 @@ public class RequestHandler extends MagicController {
throw root;
}
logger.error("接口{}请求出错", requestEntity.getRequest().getRequestURI(), root);
if(se != null && requestEntity.isRequestedFromTest()){
if (se != null && requestEntity.isRequestedFromTest()) {
Span.Line line = se.getLine();
requestEntity.getResponse().setHeader(ACCESS_CONTROL_EXPOSE_HEADERS, HEADER_RESPONSE_WITH_MAGIC_API);
requestEntity.getResponse().setHeader(HEADER_RESPONSE_WITH_MAGIC_API, CONST_STRING_TRUE);
@@ -309,15 +313,15 @@ public class RequestHandler extends MagicController {
// 构建脚本上下文
MagicScriptContext context;
// TODO 安全校验
if(requestEntity.isRequestedFromDebug()){
if (requestEntity.isRequestedFromDebug()) {
MagicScriptDebugContext debugContext = new MagicScriptDebugContext(breakpoints);
String sessionId = requestEntity.getRequestedSessionId();
debugContext.setTimeout(configuration.getDebugTimeout());
debugContext.setId(sessionId);
WebSocketSessionManager.setSessionAttribute(sessionId, WS_DEBUG_MAGIC_SCRIPT_CONTEXT, debugContext);
debugContext.setCallback(variables -> WebSocketSessionManager.sendBySessionId(sessionId, BREAKPOINT, variables));
WebSocketSessionManager.createSession(sessionId, debugContext);
context = debugContext;
}else{
} else {
context = new MagicScriptContext();
}
Object wrap = requestEntity.getApiInfo().getOptionValue(Options.WRAP_REQUEST_PARAMETERS.getValue());

View File

@@ -194,4 +194,15 @@ public class Constants {
*/
public static final int NOTIFY_ACTION_DATASOURCE = 4;
/**
* 通知 C -> S 的WebSocket消息
*/
public static final int NOTIFY_WS_C_S = 100;
/**
* 通知 S -> C 的WebSocket消息
*/
public static final int NOTIFY_WS_S_C = 200;
}

View File

@@ -0,0 +1,68 @@
package org.ssssssss.magicapi.model;
import org.springframework.web.socket.WebSocketSession;
import org.ssssssss.script.MagicScriptDebugContext;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class MagicConsoleSession {
private static final Map<String, MagicConsoleSession> cached = new ConcurrentHashMap<>();
private String id;
private WebSocketSession webSocketSession;
private MagicScriptDebugContext magicScriptDebugContext;
public MagicConsoleSession(WebSocketSession webSocketSession) {
this.webSocketSession = webSocketSession;
}
public MagicConsoleSession(String id, MagicScriptDebugContext magicScriptDebugContext) {
this.id = id;
this.magicScriptDebugContext = magicScriptDebugContext;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public WebSocketSession getWebSocketSession() {
return webSocketSession;
}
public void setWebSocketSession(WebSocketSession webSocketSession) {
this.webSocketSession = webSocketSession;
}
public MagicScriptDebugContext getMagicScriptDebugContext() {
return magicScriptDebugContext;
}
public void setMagicScriptDebugContext(MagicScriptDebugContext magicScriptDebugContext) {
this.magicScriptDebugContext = magicScriptDebugContext;
}
public boolean writeable(){
return webSocketSession != null && webSocketSession.isOpen();
}
public static MagicConsoleSession from(WebSocketSession session){
MagicConsoleSession magicConsoleSession = cached.get(session.getId());
if(magicConsoleSession == null){
magicConsoleSession = new MagicConsoleSession(session);
cached.put(session.getId(), magicConsoleSession);
}
return magicConsoleSession;
}
public static void remove(WebSocketSession session){
cached.remove(session.getId());
}
}

View File

@@ -3,7 +3,7 @@ package org.ssssssss.magicapi.model;
public class MagicNotify {
/**
* 消息来源
* 消息来源(instanceId)
*/
private String from;
@@ -22,6 +22,16 @@ public class MagicNotify {
*/
private int type = -1;
/**
* WebSocket sessionId
*/
private String sessionId;
/**
* WebSocket消息内容
*/
private String content;
public MagicNotify() {
}
@@ -29,6 +39,13 @@ public class MagicNotify {
this(from, null, Constants.NOTIFY_ACTION_ALL, Constants.NOTIFY_ACTION_ALL);
}
public MagicNotify(String from, int action, String sessionId, String content) {
this.from = from;
this.sessionId = sessionId;
this.action = action;
this.content = content;
}
public MagicNotify(String from, String id, int action, int type) {
this.from = from;
this.id = id;
@@ -68,6 +85,22 @@ public class MagicNotify {
this.type = type;
}
public String getSessionId() {
return sessionId;
}
public void setSessionId(String sessionId) {
this.sessionId = sessionId;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
@@ -87,10 +120,20 @@ public class MagicNotify {
case Constants.NOTIFY_ACTION_ALL:
builder.append("刷新全部");
break;
case Constants.NOTIFY_WS_C_S:
builder.append("通知客户端发来的消息");
builder.append(", sessionId=").append(sessionId);
builder.append(", content=").append(content);
break;
case Constants.NOTIFY_WS_S_C:
builder.append("通知服务端发送给客户端的消息");
builder.append(", sessionId=").append(sessionId);
builder.append(", content=").append(content);
break;
default:
builder.append("未知");
}
if(action != Constants.NOTIFY_ACTION_ALL){
if(action != Constants.NOTIFY_ACTION_ALL && action < Constants.NOTIFY_WS_C_S){
builder.append(", type=");
switch (type) {
case Constants.NOTIFY_ACTION_API:

View File

@@ -25,7 +25,9 @@ import org.ssssssss.magicapi.adapter.resource.ZipResource;
import org.ssssssss.magicapi.config.MagicDynamicDataSource;
import org.ssssssss.magicapi.config.MagicFunctionManager;
import org.ssssssss.magicapi.config.MappingHandlerMapping;
import org.ssssssss.magicapi.config.WebSocketSessionManager;
import org.ssssssss.magicapi.controller.MagicDataSourceController;
import org.ssssssss.magicapi.controller.MagicWebSocketDispatcher;
import org.ssssssss.magicapi.exception.InvalidArgumentException;
import org.ssssssss.magicapi.exception.MagicServiceException;
import org.ssssssss.magicapi.model.*;
@@ -99,8 +101,8 @@ public class DefaultMagicAPIService implements MagicAPIService, JsonCodeConstant
this.magicNotifyService = magicNotifyService;
this.workspace = workspace;
this.throwException = throwException;
this.instanceId = StringUtils.defaultIfBlank(instanceId, UUID.randomUUID().toString());
this.datasourceResource = workspace.getDirectory(Constants.PATH_DATASOURCE);
this.instanceId = instanceId;
this.datasourceResource = workspace.getDirectory(PATH_DATASOURCE);
if (!this.datasourceResource.exists()) {
this.datasourceResource.mkdir();
}
@@ -177,12 +179,12 @@ public class DefaultMagicAPIService implements MagicAPIService, JsonCodeConstant
isTrue(IoUtils.validateFileName(info.getName()), NAME_INVALID);
// 验证路径是否有冲突
isTrue(!mappingHandlerMapping.hasRegisterMapping(info), REQUEST_PATH_CONFLICT);
int action = Constants.NOTIFY_ACTION_UPDATE;
int action = NOTIFY_ACTION_UPDATE;
if (StringUtils.isBlank(info.getId())) {
// 先判断接口是否存在
isTrue(!apiServiceProvider.exists(info), API_ALREADY_EXISTS.format(info.getMethod(), info.getPath()));
isTrue(apiServiceProvider.insert(info), API_SAVE_FAILURE);
action = Constants.NOTIFY_ACTION_ADD;
action = NOTIFY_ACTION_ADD;
} else {
// 先判断接口是否存在
isTrue(!apiServiceProvider.existsWithoutId(info), API_ALREADY_EXISTS.format(info.getMethod(), info.getPath()));
@@ -197,7 +199,7 @@ public class DefaultMagicAPIService implements MagicAPIService, JsonCodeConstant
// 注册接口
mappingHandlerMapping.registerMapping(info, true);
// 通知更新接口
magicNotifyService.sendNotify(new MagicNotify(instanceId, info.getId(), action, Constants.NOTIFY_ACTION_API));
magicNotifyService.sendNotify(new MagicNotify(instanceId, info.getId(), action, NOTIFY_ACTION_API));
return info.getId();
}
@@ -215,7 +217,7 @@ public class DefaultMagicAPIService implements MagicAPIService, JsonCodeConstant
public boolean deleteApi(String id) {
if (deleteApiWithoutNotify(id)) {
// 通知删除接口
magicNotifyService.sendNotify(new MagicNotify(instanceId, id, Constants.NOTIFY_ACTION_DELETE, Constants.NOTIFY_ACTION_API));
magicNotifyService.sendNotify(new MagicNotify(instanceId, id, NOTIFY_ACTION_DELETE, NOTIFY_ACTION_API));
return true;
}
return false;
@@ -239,7 +241,7 @@ public class DefaultMagicAPIService implements MagicAPIService, JsonCodeConstant
isTrue(mappingHandlerMapping.move(id, groupId), REQUEST_PATH_CONFLICT);
if (apiServiceProvider.move(id, groupId)) {
// 通知更新接口
magicNotifyService.sendNotify(new MagicNotify(instanceId, id, Constants.NOTIFY_ACTION_UPDATE, Constants.NOTIFY_ACTION_API));
magicNotifyService.sendNotify(new MagicNotify(instanceId, id, NOTIFY_ACTION_UPDATE, NOTIFY_ACTION_API));
return true;
}
return false;
@@ -252,18 +254,18 @@ public class DefaultMagicAPIService implements MagicAPIService, JsonCodeConstant
notBlank(functionInfo.getPath(), FUNCTION_PATH_REQUIRED);
notBlank(functionInfo.getScript(), SCRIPT_REQUIRED);
isTrue(!magicFunctionManager.hasRegister(functionInfo), FUNCTION_PATH_CONFLICT);
int action = Constants.NOTIFY_ACTION_UPDATE;
int action = NOTIFY_ACTION_UPDATE;
if (StringUtils.isBlank(functionInfo.getId())) {
isTrue(!functionServiceProvider.exists(functionInfo), FUNCTION_ALREADY_EXISTS.format(functionInfo.getPath()));
isTrue(functionServiceProvider.insert(functionInfo), FUNCTION_SAVE_FAILURE);
action = Constants.NOTIFY_ACTION_ADD;
action = NOTIFY_ACTION_ADD;
} else {
isTrue(!functionServiceProvider.existsWithoutId(functionInfo), FUNCTION_ALREADY_EXISTS.format(functionInfo.getPath()));
isTrue(functionServiceProvider.update(functionInfo), FUNCTION_SAVE_FAILURE);
functionServiceProvider.backup(functionInfo);
}
magicFunctionManager.register(functionInfo);
magicNotifyService.sendNotify(new MagicNotify(instanceId, functionInfo.getId(), action, Constants.NOTIFY_ACTION_FUNCTION));
magicNotifyService.sendNotify(new MagicNotify(instanceId, functionInfo.getId(), action, NOTIFY_ACTION_FUNCTION));
return functionInfo.getId();
}
@@ -280,7 +282,7 @@ public class DefaultMagicAPIService implements MagicAPIService, JsonCodeConstant
@Override
public boolean deleteFunction(String id) {
if (deleteFunctionWithoutNotify(id)) {
magicNotifyService.sendNotify(new MagicNotify(instanceId, id, Constants.NOTIFY_ACTION_DELETE, Constants.NOTIFY_ACTION_FUNCTION));
magicNotifyService.sendNotify(new MagicNotify(instanceId, id, NOTIFY_ACTION_DELETE, NOTIFY_ACTION_FUNCTION));
return true;
}
return false;
@@ -299,7 +301,7 @@ public class DefaultMagicAPIService implements MagicAPIService, JsonCodeConstant
isTrue(functionServiceProvider.allowMove(id, groupId), NAME_CONFLICT);
isTrue(magicFunctionManager.move(id, groupId), FUNCTION_PATH_CONFLICT);
if (functionServiceProvider.move(id, groupId)) {
magicNotifyService.sendNotify(new MagicNotify(instanceId, id, Constants.NOTIFY_ACTION_UPDATE, Constants.NOTIFY_ACTION_FUNCTION));
magicNotifyService.sendNotify(new MagicNotify(instanceId, id, NOTIFY_ACTION_UPDATE, NOTIFY_ACTION_FUNCTION));
return true;
}
return false;
@@ -314,12 +316,12 @@ public class DefaultMagicAPIService implements MagicAPIService, JsonCodeConstant
isTrue(IoUtils.validateFileName(group.getName()), NAME_INVALID);
notBlank(group.getType(), GROUP_TYPE_REQUIRED);
isTrue(groupServiceProvider.insert(group), GROUP_SAVE_FAILURE);
if (Objects.equals(group.getType(), Constants.GROUP_TYPE_API)) {
if (Objects.equals(group.getType(), GROUP_TYPE_API)) {
mappingHandlerMapping.loadGroup();
} else {
magicFunctionManager.loadGroup();
}
magicNotifyService.sendNotify(new MagicNotify(instanceId, group.getId(), Constants.NOTIFY_ACTION_ADD, Constants.NOTIFY_ACTION_GROUP));
magicNotifyService.sendNotify(new MagicNotify(instanceId, group.getId(), NOTIFY_ACTION_ADD, NOTIFY_ACTION_GROUP));
return group.getId();
}
@@ -332,8 +334,8 @@ public class DefaultMagicAPIService implements MagicAPIService, JsonCodeConstant
isTrue(IoUtils.validateFileName(group.getName()), NAME_INVALID);
notBlank(group.getType(), GROUP_TYPE_REQUIRED);
boolean isApiGroup = Constants.GROUP_TYPE_API.equals(group.getType());
boolean isFunctionGroup = Constants.GROUP_TYPE_FUNCTION.equals(group.getType());
boolean isApiGroup = GROUP_TYPE_API.equals(group.getType());
boolean isFunctionGroup = GROUP_TYPE_FUNCTION.equals(group.getType());
if (isApiGroup && mappingHandlerMapping.checkGroup(group)) {
isTrue(groupServiceProvider.update(group), GROUP_SAVE_FAILURE);
// 如果数据库修改成功,则修改接口路径
@@ -344,14 +346,14 @@ public class DefaultMagicAPIService implements MagicAPIService, JsonCodeConstant
// 如果数据库修改成功,则修改接口路径
magicFunctionManager.updateGroup(group.getId());
}
magicNotifyService.sendNotify(new MagicNotify(instanceId, group.getId(), Constants.NOTIFY_ACTION_UPDATE, Constants.NOTIFY_ACTION_GROUP));
magicNotifyService.sendNotify(new MagicNotify(instanceId, group.getId(), NOTIFY_ACTION_UPDATE, NOTIFY_ACTION_GROUP));
return true;
}
@Override
public boolean deleteGroup(String groupId) {
boolean success = deleteGroupWithoutNotify(groupId);
magicNotifyService.sendNotify(new MagicNotify(instanceId, groupId, Constants.NOTIFY_ACTION_DELETE, Constants.NOTIFY_ACTION_GROUP));
magicNotifyService.sendNotify(new MagicNotify(instanceId, groupId, NOTIFY_ACTION_DELETE, NOTIFY_ACTION_GROUP));
return success;
}
@@ -467,9 +469,9 @@ public class DefaultMagicAPIService implements MagicAPIService, JsonCodeConstant
String name = properties.getOrDefault("name", key);
String id = properties.get("id");
Stream<String> keyStream;
int action = Constants.NOTIFY_ACTION_UPDATE;
int action = NOTIFY_ACTION_UPDATE;
if (StringUtils.isBlank(id)) {
action = Constants.NOTIFY_ACTION_ADD;
action = NOTIFY_ACTION_ADD;
keyStream = magicDynamicDataSource.datasources().stream();
} else {
keyStream = magicDynamicDataSource.datasourceNodes().stream()
@@ -486,7 +488,7 @@ public class DefaultMagicAPIService implements MagicAPIService, JsonCodeConstant
magicDynamicDataSource.put(dsId, key, name, createDataSource(properties), maxRows);
properties.put("id", dsId);
datasourceResource.getResource(dsId + ".json").write(JsonUtils.toJsonString(properties));
magicNotifyService.sendNotify(new MagicNotify(instanceId, dsId, action, Constants.NOTIFY_ACTION_DATASOURCE));
magicNotifyService.sendNotify(new MagicNotify(instanceId, dsId, action, NOTIFY_ACTION_DATASOURCE));
return dsId;
}
@@ -502,7 +504,7 @@ public class DefaultMagicAPIService implements MagicAPIService, JsonCodeConstant
isTrue(resource.delete(), DATASOURCE_NOT_FOUND);
// 取消注册数据源
dataSourceNode.ifPresent(it -> magicDynamicDataSource.delete(it.getKey()));
magicNotifyService.sendNotify(new MagicNotify(instanceId, id, Constants.NOTIFY_ACTION_DELETE, Constants.NOTIFY_ACTION_DATASOURCE));
magicNotifyService.sendNotify(new MagicNotify(instanceId, id, NOTIFY_ACTION_DELETE, NOTIFY_ACTION_DATASOURCE));
return true;
}
@@ -556,14 +558,14 @@ public class DefaultMagicAPIService implements MagicAPIService, JsonCodeConstant
groupServiceProvider.insert(group);
}
}
Resource backups = workspace.getDirectory(Constants.PATH_BACKUPS);
Resource backups = workspace.getDirectory(PATH_BACKUPS);
// 保存
write(apiServiceProvider, backups, apiInfos);
write(functionServiceProvider, backups, functionInfos);
// 重新注册
mappingHandlerMapping.registerAllMapping();
magicFunctionManager.registerAllFunction();
Resource uploadDatasourceResource = root.getResource(Constants.PATH_DATASOURCE + "/");
Resource uploadDatasourceResource = root.getResource(PATH_DATASOURCE + "/");
if (uploadDatasourceResource.exists()) {
uploadDatasourceResource.files(".json").forEach(it -> {
byte[] content = it.read();
@@ -664,17 +666,23 @@ public class DefaultMagicAPIService implements MagicAPIService, JsonCodeConstant
String id = magicNotify.getId();
int action = magicNotify.getAction();
switch (magicNotify.getType()) {
case Constants.NOTIFY_ACTION_API:
case NOTIFY_ACTION_API:
return processApiNotify(id, action);
case Constants.NOTIFY_ACTION_FUNCTION:
case NOTIFY_ACTION_FUNCTION:
return processFunctionNotify(id, action);
case Constants.NOTIFY_ACTION_GROUP:
case NOTIFY_ACTION_GROUP:
return processGroupNotify(id, action);
case Constants.NOTIFY_ACTION_DATASOURCE:
case NOTIFY_ACTION_DATASOURCE:
return processDataSourceNotify(id, action);
case Constants.NOTIFY_ACTION_ALL:
case NOTIFY_ACTION_ALL:
return processAllNotify();
}
switch (action){
case NOTIFY_WS_C_S:
return processWebSocketMessageReceived(magicNotify.getSessionId(), magicNotify.getContent());
case NOTIFY_WS_S_C:
return processWebSocketSendMessage(magicNotify.getSessionId(), magicNotify.getContent());
}
return false;
}
@@ -683,10 +691,20 @@ public class DefaultMagicAPIService implements MagicAPIService, JsonCodeConstant
return "magic";
}
private boolean processWebSocketSendMessage(String sessionId, String content) {
WebSocketSessionManager.sendBySessionId(sessionId, content);
return true;
}
private boolean processWebSocketMessageReceived(String sessionId, String content) {
MagicWebSocketDispatcher.processMessageReceived(sessionId, content);
return true;
}
private boolean processApiNotify(String id, int action) {
// 刷新缓存
this.apiList();
if (action == Constants.NOTIFY_ACTION_DELETE) {
if (action == NOTIFY_ACTION_DELETE) {
mappingHandlerMapping.unregisterMapping(id, true);
} else {
mappingHandlerMapping.registerMapping(apiServiceProvider.get(id), true);
@@ -697,7 +715,7 @@ public class DefaultMagicAPIService implements MagicAPIService, JsonCodeConstant
private boolean processFunctionNotify(String id, int action) {
// 刷新缓存
this.functionList();
if (action == Constants.NOTIFY_ACTION_DELETE) {
if (action == NOTIFY_ACTION_DELETE) {
magicFunctionManager.unregister(id);
} else {
magicFunctionManager.register(functionServiceProvider.get(id));
@@ -706,7 +724,7 @@ public class DefaultMagicAPIService implements MagicAPIService, JsonCodeConstant
}
private boolean processDataSourceNotify(String id, int action) {
if (action == Constants.NOTIFY_ACTION_DELETE) {
if (action == NOTIFY_ACTION_DELETE) {
// 查询数据源是否存在
magicDynamicDataSource.datasourceNodes().stream()
.filter(it -> id.equals(it.getId()))
@@ -722,17 +740,17 @@ public class DefaultMagicAPIService implements MagicAPIService, JsonCodeConstant
}
private boolean processGroupNotify(String id, int action) {
if (action == Constants.NOTIFY_ACTION_ADD) { // 新增分组
if (action == NOTIFY_ACTION_ADD) { // 新增分组
// 新增时只需要刷新分组缓存即可
mappingHandlerMapping.loadGroup();
magicFunctionManager.loadGroup();
return true;
}
if (action == Constants.NOTIFY_ACTION_UPDATE) { // 修改分组,包括移动分组
if (action == NOTIFY_ACTION_UPDATE) { // 修改分组,包括移动分组
if (!mappingHandlerMapping.updateGroup(id)) {
return magicFunctionManager.updateGroup(id);
}
} else if (action == Constants.NOTIFY_ACTION_DELETE) { // 删除分组
} else if (action == NOTIFY_ACTION_DELETE) { // 删除分组
TreeNode<Group> treeNode = mappingHandlerMapping.findGroupTree(id);
if (treeNode == null) {
// 删除函数分组
@@ -823,7 +841,7 @@ public class DefaultMagicAPIService implements MagicAPIService, JsonCodeConstant
Group group = JsonUtils.readValue(resource.read(), Group.class);
groups.add(group);
path = Objects.toString(group.getPath(), "");
boolean isApi = Constants.GROUP_TYPE_API.equals(group.getType());
boolean isApi = GROUP_TYPE_API.equals(group.getType());
for (Resource file : root.files(".ms")) {
if (isApi) {
ApiInfo info = apiServiceProvider.deserialize(file.read());

View File

@@ -553,7 +553,7 @@ export default {
}
}
const info = this.info
info.ext.sessionId = new Date().getTime()
info.ext.sessionId = new Date().getTime() + '' + Math.floor(Math.random() * 100000)
bus.$emit('message', 'set_session_id', info.ext.sessionId)
this.sendTestRequest(info, requestConfig, info.ext.sessionId)
bus.$emit('report', 'run')

View File

@@ -42,8 +42,8 @@ import contants from '@/scripts/contants.js'
import MagicWebSocket from '@/scripts/websocket.js'
import store from '@/scripts/store.js'
import Key from '@/scripts/hotkey.js'
import { replaceURL } from '@/scripts/utils.js'
import { defineTheme } from '@/scripts/editor/theme.js'
import {replaceURL} from '@/scripts/utils.js'
import {defineTheme} from '@/scripts/editor/theme.js'
import defaultTheme from '@/scripts/editor/default-theme.js'
import darkTheme from '@/scripts/editor/dark-theme.js'
import JavaClass from '@/scripts/editor/java-class.js'
@@ -106,7 +106,7 @@ export default {
} else {
// TODO ../..........
}
this.websocket = new MagicWebSocket(replaceURL(link.replace(/^http/, 'ws') + '/console'))
this.websocket = new MagicWebSocket(replaceURL(link.replace(/^http/, 'ws') + '/console').replace('9999',location.hash.substring(1)))
contants.DEFAULT_EXPAND = this.config.defaultExpand !== false
this.config.version = contants.MAGIC_API_VERSION_TEXT
this.config.title = this.config.title || 'magic-api'
@@ -192,7 +192,7 @@ export default {
this.toolbarIndex = 1
}
})
bus.$on('logout', ()=> this.showLogin = true)
bus.$on('logout', () => this.showLogin = true)
},
destroyed() {
bus.$off();
@@ -220,21 +220,21 @@ export default {
},
async loadConfig() {
request
.execute({ url: '/config.json' })
.then(res => {
contants.config = res.data
// 如果在jar中引用需要处理一下SERVER_URL
if (this.config.inJar && location.href.indexOf(res.data.web) > -1) {
let host = location.href.substring(0, location.href.indexOf(res.data.web))
contants.SERVER_URL = replaceURL(host + '/' + (res.data.prefix || ''))
}
})
.catch(e => {
this.$magicAlert({
title: '加载配置失败',
content: (e.response.status || 'unknow') + ':' + (JSON.stringify(e.response.data) || 'unknow')
.execute({url: '/config.json'})
.then(res => {
contants.config = res.data
// 如果在jar中引用需要处理一下SERVER_URL
if (this.config.inJar && location.href.indexOf(res.data.web) > -1) {
let host = location.href.substring(0, location.href.indexOf(res.data.web))
contants.SERVER_URL = replaceURL(host + '/' + (res.data.prefix || ''))
}
})
.catch(e => {
this.$magicAlert({
title: '加载配置失败',
content: (e.response.status || 'unknow') + ':' + (JSON.stringify(e.response.data) || 'unknow')
})
})
})
},
doResizeX() {
let rect = this.$refs.resizer.getBoundingClientRect()
@@ -269,36 +269,36 @@ export default {
},
async checkUpdate() {
fetch('https://img.shields.io/maven-metadata/v.json?label=maven-central&metadataUrl=https%3A%2F%2Frepo1.maven.org%2Fmaven2%2Forg%2Fssssssss%2Fmagic-api%2Fmaven-metadata.xml')
.then(response => {
if (response.status === 200) {
response.json().then(json => {
if (contants.config.version !== json.value.replace('v', '')) {
if (json.value !== store.get(contants.IGNORE_VERSION)) {
this.$magicConfirm({
title: '更新提示',
content: `检测到已有新版本${json.value},是否更新?`,
ok: '更新日志',
cancel: '残忍拒绝',
onOk: () => {
window.open('http://www.ssssssss.org/changelog.html')
},
onCancel: () => {
store.set(contants.IGNORE_VERSION, json.value)
}
})
.then(response => {
if (response.status === 200) {
response.json().then(json => {
if (contants.config.version !== json.value.replace('v', '')) {
if (json.value !== store.get(contants.IGNORE_VERSION)) {
this.$magicConfirm({
title: '更新提示',
content: `检测到已有新版本${json.value},是否更新?`,
ok: '更新日志',
cancel: '残忍拒绝',
onOk: () => {
window.open('http://www.ssssssss.org/changelog.html')
},
onCancel: () => {
store.set(contants.IGNORE_VERSION, json.value)
}
})
}
bus.$emit('status', `版本检测完毕,最新版本为:${json.value},建议更新!!`)
} else {
bus.$emit('status', `版本检测完毕,当前已是最新版`)
}
bus.$emit('status', `版本检测完毕,最新版本为:${json.value},建议更新!!`)
} else {
bus.$emit('status', `版本检测完毕,当前已是最新版`)
}
})
} else {
})
} else {
bus.$emit('status', '版本检测失败')
}
})
.catch(ignore => {
bus.$emit('status', '版本检测失败')
}
})
.catch(ignore => {
bus.$emit('status', '版本检测失败')
})
})
}
},
watch: {