MagicWorkbenchController.java 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. package org.ssssssss.magicapi.controller;
  2. import org.apache.commons.lang3.StringUtils;
  3. import org.slf4j.Logger;
  4. import org.slf4j.LoggerFactory;
  5. import org.springframework.core.io.ClassPathResource;
  6. import org.springframework.core.io.InputStreamResource;
  7. import org.springframework.http.HttpHeaders;
  8. import org.springframework.http.MediaType;
  9. import org.springframework.http.ResponseEntity;
  10. import org.springframework.util.ResourceUtils;
  11. import org.springframework.web.bind.annotation.RequestMapping;
  12. import org.springframework.web.bind.annotation.ResponseBody;
  13. import org.springframework.web.multipart.MultipartFile;
  14. import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
  15. import org.ssssssss.magicapi.adapter.Resource;
  16. import org.ssssssss.magicapi.adapter.resource.ZipResource;
  17. import org.ssssssss.magicapi.config.MagicConfiguration;
  18. import org.ssssssss.magicapi.config.Valid;
  19. import org.ssssssss.magicapi.interceptor.RequestInterceptor;
  20. import org.ssssssss.magicapi.logging.MagicLoggerContext;
  21. import org.ssssssss.magicapi.model.*;
  22. import org.ssssssss.magicapi.modules.ResponseModule;
  23. import org.ssssssss.magicapi.provider.GroupServiceProvider;
  24. import org.ssssssss.magicapi.provider.StoreServiceProvider;
  25. import org.ssssssss.magicapi.utils.JsonUtils;
  26. import org.ssssssss.magicapi.utils.MD5Utils;
  27. import org.ssssssss.magicapi.utils.PathUtils;
  28. import javax.servlet.http.HttpServletRequest;
  29. import javax.servlet.http.HttpServletResponse;
  30. import java.io.ByteArrayOutputStream;
  31. import java.io.File;
  32. import java.io.IOException;
  33. import java.nio.file.Files;
  34. import java.nio.file.Paths;
  35. import java.util.*;
  36. import java.util.stream.Collectors;
  37. import java.util.stream.Stream;
  38. public class MagicWorkbenchController extends MagicController {
  39. private static final Logger logger = LoggerFactory.getLogger(MagicWorkbenchController.class);
  40. public MagicWorkbenchController(MagicConfiguration configuration) {
  41. super(configuration);
  42. }
  43. /**
  44. * 登录
  45. */
  46. @RequestMapping("/login")
  47. @ResponseBody
  48. public JsonBean<Boolean> login(String username, String password, HttpServletRequest request, HttpServletResponse response) {
  49. if (username != null && password != null && Objects.equals(username, configuration.getUsername()) && Objects.equals(password, configuration.getPassword())) {
  50. response.setHeader(configuration.getTokenKey(), MD5Utils.encrypt(String.format("%s||%s", username, password)));
  51. response.setHeader(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, configuration.getTokenKey());
  52. return new JsonBean<>(true);
  53. } else if (allowVisit(request, null)) {
  54. return new JsonBean<>(true);
  55. }
  56. return new JsonBean<>(false);
  57. }
  58. /**
  59. * 创建控制台输出
  60. */
  61. @RequestMapping("/console")
  62. public SseEmitter console() throws IOException {
  63. String sessionId = UUID.randomUUID().toString().replace("-", "");
  64. SseEmitter emitter = MagicLoggerContext.createEmitter(sessionId);
  65. emitter.send(SseEmitter.event().data(sessionId).name("create"));
  66. return emitter;
  67. }
  68. @RequestMapping("/options")
  69. @ResponseBody
  70. public JsonBean<List<Map<String, String>>> options() {
  71. return new JsonBean<>(Stream.of(Options.values()).map(item -> Collections.singletonMap(item.getValue(), item.getName())).collect(Collectors.toList()));
  72. }
  73. @RequestMapping(value = "/config-js")
  74. @ResponseBody
  75. public ResponseEntity<?> configjs() {
  76. ResponseEntity.BodyBuilder responseBuilder = ResponseEntity.ok().contentType(MediaType.parseMediaType("application/javascript"));
  77. if (configuration.getEditorConfig() != null) {
  78. try {
  79. String path = configuration.getEditorConfig();
  80. if(path.startsWith(ResourceUtils.CLASSPATH_URL_PREFIX)){
  81. path = path.substring(ResourceUtils.CLASSPATH_URL_PREFIX.length());
  82. return responseBuilder.body(new InputStreamResource(new ClassPathResource(path).getInputStream()));
  83. }
  84. File file = ResourceUtils.getFile(configuration.getEditorConfig());
  85. return responseBuilder.body(Files.readAllBytes(Paths.get(file.toURI())));
  86. } catch (IOException e) {
  87. logger.warn("读取编辑器配置文件{}失败", configuration.getEditorConfig());
  88. }
  89. }
  90. return responseBuilder.body("var MAGIC_EDITOR_CONFIG = {}".getBytes());
  91. }
  92. @RequestMapping("/download")
  93. @Valid(authorization = RequestInterceptor.Authorization.DOWNLOAD)
  94. @ResponseBody
  95. public ResponseEntity<?> download(String groupId) throws IOException {
  96. if (StringUtils.isBlank(groupId)) {
  97. return download(configuration.getWorkspace(), "magic-api-all.zip");
  98. } else {
  99. Resource resource = configuration.getGroupServiceProvider().getGroupResource(groupId);
  100. notNull(resource, GROUP_NOT_FOUND);
  101. return download(resource, "magic-api-group.zip");
  102. }
  103. }
  104. @RequestMapping("/upload")
  105. @Valid(readonly = false, authorization = RequestInterceptor.Authorization.UPLOAD)
  106. @ResponseBody
  107. public JsonBean<Boolean> upload(MultipartFile file, String mode) throws IOException {
  108. notNull(file, FILE_IS_REQUIRED);
  109. ZipResource root = new ZipResource(file.getInputStream());
  110. Set<String> apiPaths = new HashSet<>();
  111. Set<String> functionPaths = new HashSet<>();
  112. Set<Group> groups = new HashSet<>();
  113. Set<ApiInfo> apiInfos = new HashSet<>();
  114. Set<FunctionInfo> functionInfos = new HashSet<>();
  115. // 检查上传资源中是否有冲突
  116. isTrue(readPaths(groups, apiPaths, functionPaths, apiInfos, functionInfos, "/", root), UPLOAD_PATH_CONFLICT);
  117. // 判断是否是强制上传
  118. if (!"force".equals(mode)) {
  119. // 检测与已注册的接口和函数是否有冲突
  120. isTrue(!configuration.getMappingHandlerMapping().hasRegister(apiPaths), UPLOAD_PATH_CONFLICT.format("接口"));
  121. isTrue(!configuration.getMagicFunctionManager().hasRegister(apiPaths), UPLOAD_PATH_CONFLICT.format("函数"));
  122. }
  123. Resource item = root.getResource("group.json");
  124. GroupServiceProvider groupServiceProvider = configuration.getGroupServiceProvider();
  125. if (item.exists()) {
  126. Group group = groupServiceProvider.readGroup(item);
  127. // 检查分组是否存在
  128. isTrue("0".equals(group.getParentId()) || groupServiceProvider.getGroupResource(group.getParentId()).exists(), GROUP_NOT_FOUND);
  129. groups.removeIf(it -> it.getId().equalsIgnoreCase(group.getId()));
  130. }
  131. for (Group group : groups) {
  132. Resource groupResource = groupServiceProvider.getGroupResource(group.getId());
  133. if (groupResource!= null && groupResource.exists()) {
  134. groupServiceProvider.update(group);
  135. } else {
  136. groupServiceProvider.insert(group);
  137. }
  138. }
  139. Resource backups = configuration.getWorkspace().getDirectory("backups");
  140. // 保存
  141. write(configuration.getMagicApiService(),backups,apiInfos);
  142. write(configuration.getFunctionServiceProvider(),backups,functionInfos);
  143. // 重新注册
  144. configuration.getMappingHandlerMapping().registerAllMapping();
  145. configuration.getMagicFunctionManager().registerAllFunction();
  146. return new JsonBean<>(SUCCESS, true);
  147. }
  148. private <T extends MagicEntity> void write(StoreServiceProvider<T> provider, Resource backups, Set<T> infos) {
  149. for (T info : infos) {
  150. Resource resource = configuration.getGroupServiceProvider().getGroupResource(info.getGroupId());
  151. resource = resource.getResource(info.getName() + ".ms");
  152. byte[] content = provider.serialize(info);
  153. resource.write(content);
  154. Resource directory = backups.getDirectory(info.getId());
  155. if (!directory.exists()) {
  156. directory.mkdir();
  157. }
  158. directory.getResource(System.currentTimeMillis() + ".ms").write(content);
  159. resource.write(content);
  160. }
  161. }
  162. private boolean readPaths(Set<Group> groups, Set<String> apiPaths, Set<String> functionPaths, Set<ApiInfo> apiInfos, Set<FunctionInfo> functionInfos, String parentPath, Resource root) {
  163. Resource resource = root.getResource("group.json");
  164. String path = "";
  165. if (resource.exists()) {
  166. Group group = JsonUtils.readValue(resource.read(), Group.class);
  167. groups.add(group);
  168. path = Objects.toString(group.getPath(), "");
  169. boolean isApi = "1".equals(group.getType());
  170. for (Resource file : root.files(".ms")) {
  171. boolean conflict;
  172. if (isApi) {
  173. ApiInfo info = configuration.getMagicApiService().deserialize(file.read());
  174. apiInfos.add(info);
  175. conflict = !apiPaths.add(Objects.toString(info.getMethod(), "GET") + ":" + PathUtils.replaceSlash(parentPath + "/" + path + "/" + info.getPath()));
  176. } else {
  177. FunctionInfo info = configuration.getFunctionServiceProvider().deserialize(file.read());
  178. functionInfos.add(info);
  179. conflict = !functionPaths.add(PathUtils.replaceSlash(parentPath + "/" + path + "/" + info.getPath()));
  180. }
  181. if (conflict) {
  182. return false;
  183. }
  184. }
  185. }
  186. for (Resource directory : root.dirs()) {
  187. if (!readPaths(groups, apiPaths, functionPaths, apiInfos, functionInfos, PathUtils.replaceSlash(parentPath + "/" + path), directory)) {
  188. return false;
  189. }
  190. }
  191. return true;
  192. }
  193. private ResponseEntity<?> download(Resource resource, String filename) throws IOException {
  194. ByteArrayOutputStream os = new ByteArrayOutputStream();
  195. resource.export(os, "backups");
  196. return ResponseModule.download(os.toByteArray(), filename);
  197. }
  198. }