Commit 02897568 authored by liang.tang's avatar liang.tang
Browse files

magic-api

parents
Pipeline #222 failed with stages
in 0 seconds
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.ssssssss</groupId>
<artifactId>magic-api-parent</artifactId>
<version>2.1.1</version>
</parent>
<artifactId>magic-api</artifactId>
<packaging>jar</packaging>
<name>magic-api</name>
<description>auto generate http api</description>
<dependencies>
<dependency>
<groupId>org.ssssssss</groupId>
<artifactId>magic-script</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
</dependency>
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>
package org.ssssssss.magicapi.backup.model;
/**
* 备份记录
*
* @author mxd
*/
public class Backup {
/**
* 记录ID
*/
private String id;
/**
* 备份时间
*/
private Long createDate = 0L;
/**
* 标签,只允许有一个
*/
private String tag;
/**
* 备份类型,api function datasource
*/
private String type;
/**
* 原名称
*/
private String name;
/**
* 备份内容
*/
private byte[] content;
/**
* 操作人,取用户名,空为系统记录
*/
private String createBy;
public Backup() {
}
public Backup(String id, String type, String name, byte[] content) {
this.id = id;
this.type = type;
this.name = name;
this.content = content;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Long getCreateDate() {
return createDate;
}
public void setCreateDate(Long createDate) {
this.createDate = createDate;
}
public String getTag() {
return tag;
}
public void setTag(String tag) {
this.tag = tag;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public byte[] getContent() {
return content;
}
public void setContent(byte[] content) {
this.content = content;
}
public String getCreateBy() {
return createBy;
}
public void setCreateBy(String createBy) {
this.createBy = createBy;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Backup small() {
setContent(null);
return this;
}
}
package org.ssssssss.magicapi.backup.service;
import org.ssssssss.magicapi.backup.model.Backup;
import java.io.IOException;
import java.util.List;
/**
* 数据备份接口
*
* @author mxd
*/
public interface MagicBackupService {
int FETCH_SIZE = 100;
/**
* 执行备份动作
*
* @param backup 备份对象
*/
void doBackup(Backup backup);
void doBackupAll(String name, String createBy) throws IOException;
/**
* 根据时间戳查询最近的 FETCH_SIZE 条记录
*
* @param timestamp 时间戳
* @return 返回备份记录
*/
List<Backup> backupList(long timestamp);
/**
* 根据对象ID查询备份记录
*
* @param id 对象ID
* @return 返回备份记录
*/
List<Backup> backupById(String id);
/**
* 根据对象ID和备份时间查询
*/
Backup backupInfo(String id, long timestamp);
/**
* 根据标签查询备份记录
*
* @param tag 标签
* @return 返回备份记录
*/
List<Backup> backupByTag(String tag);
/**
* 删除备份
*
* @param id 对象ID
* @return 返回删除的记录数
*/
long removeBackup(String id);
/**
* 根据13位时间戳删除备份记录(清除小于该值的记录)
*
* @param timestamp 时间戳
* @return 返回删除的记录数
*/
long removeBackupByTimestamp(long timestamp);
}
package org.ssssssss.magicapi.backup.service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.event.EventListener;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.ssssssss.magicapi.core.config.MagicConfiguration;
import org.ssssssss.magicapi.core.event.FileEvent;
import org.ssssssss.magicapi.core.event.GroupEvent;
import org.ssssssss.magicapi.backup.model.Backup;
import org.ssssssss.magicapi.core.model.Group;
import org.ssssssss.magicapi.core.model.MagicEntity;
import org.ssssssss.magicapi.utils.JsonUtils;
import org.ssssssss.magicapi.utils.WebUtils;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;
/**
* 数据库备份实现
*
* @author mxd
*/
public class MagicDatabaseBackupService implements MagicBackupService {
private final static String DEFAULT_COLUMNS = "id,create_date,tag,type,name,create_by";
private final JdbcTemplate template;
private final String INSERT_SQL;
private final String FIND_BY_ID;
private final String FIND_BY_TAG;
private final String FIND_BY_TIMESTAMP;
private final String FIND_BY_ID_AND_TIMESTAMP;
private final String DELETE_BY_ID;
private final String DELETE_BY_TIMESTAMP;
private final BeanPropertyRowMapper<Backup> rowMapper = new BeanPropertyRowMapper<>(Backup.class);
private static final Logger logger = LoggerFactory.getLogger(MagicDatabaseBackupService.class);
public MagicDatabaseBackupService(JdbcTemplate template, String tableName) {
this.template = template;
this.template.setMaxRows(FETCH_SIZE);
this.INSERT_SQL = String.format("insert into %s(%s,content) values(?,?,?,?,?,?,?)", tableName, DEFAULT_COLUMNS);
this.FIND_BY_ID = String.format("select %s from %s where id = ? order by create_date desc", DEFAULT_COLUMNS, tableName);
this.DELETE_BY_ID = String.format("delete from %s where id = ?", tableName);
this.FIND_BY_TAG = String.format("select %s from %s where tag = ? order by create_date desc", DEFAULT_COLUMNS, tableName);
this.FIND_BY_TIMESTAMP = String.format("select %s from %s where create_date < ? order by create_date desc", DEFAULT_COLUMNS, tableName);
this.DELETE_BY_TIMESTAMP = String.format("delete from %s where create_date < ?", tableName);
this.FIND_BY_ID_AND_TIMESTAMP = String.format("select * from %s where id = ? and create_date = ?", tableName);
}
@Override
public void doBackupAll(String name, String createBy) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
MagicConfiguration.getMagicResourceService().export(null, null, baos);
Backup backup = new Backup();
backup.setId("full");
backup.setType("full");
backup.setName(name);
backup.setCreateBy(createBy);
backup.setContent(baos.toByteArray());
doBackup(backup);
}
@Override
public void doBackup(Backup backup) {
try {
if (backup.getCreateDate() == 0) {
backup.setCreateDate(System.currentTimeMillis());
}
if (backup.getCreateBy() == null) {
backup.setCreateBy(WebUtils.currentUserName());
}
template.update(INSERT_SQL, backup.getId(), backup.getCreateDate(), backup.getTag(), backup.getType(), backup.getName(), backup.getCreateBy(), backup.getContent());
} catch (Exception e) {
logger.warn("备份失败", e);
}
}
@Override
public List<Backup> backupList(long timestamp) {
return template.query(FIND_BY_TIMESTAMP, rowMapper, timestamp);
}
@Override
public List<Backup> backupById(String id) {
return template.query(FIND_BY_ID, rowMapper, id);
}
@Override
public Backup backupInfo(String id, long timestamp) {
return template.queryForObject(FIND_BY_ID_AND_TIMESTAMP, rowMapper, id, timestamp);
}
@Override
public List<Backup> backupByTag(String tag) {
return template.query(FIND_BY_TAG, rowMapper, tag);
}
@Override
public long removeBackup(String id) {
return template.update(DELETE_BY_ID, id);
}
@Override
public long removeBackupByTimestamp(long timestamp) {
try {
return template.update(DELETE_BY_TIMESTAMP, timestamp);
} catch (Exception e) {
logger.warn("删除备份失败", e);
return -1;
}
}
@EventListener(condition = "#event.source != T(org.ssssssss.magicapi.core.config.Constants).EVENT_SOURCE_NOTIFY")
public void onFileEvent(FileEvent event) {
switch (event.getAction()) {
case SAVE:
case CREATE:
case MOVE:
break;
default:
return;
}
MagicEntity entity = event.getEntity();
doBackup(entity.getId(), JsonUtils.toJsonBytes(entity), entity.getName(), event.getType());
}
@EventListener(condition = "#event.source != T(org.ssssssss.magicapi.core.config.Constants).EVENT_SOURCE_NOTIFY")
public void onFolderEvent(GroupEvent event) {
switch (event.getAction()) {
case SAVE:
case CREATE:
case MOVE:
break;
default:
return;
}
Group group = event.getGroup();
doBackup(group.getId(), JsonUtils.toJsonBytes(group), group.getName(), group.getType() + "-group");
}
private void doBackup(String id, byte[] content, String name, String type) {
Backup backup = new Backup();
backup.setName(name);
backup.setId(id);
backup.setContent(content);
backup.setType(type);
doBackup(backup);
}
}
package org.ssssssss.magicapi.backup.web;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.ssssssss.magicapi.backup.model.Backup;
import org.ssssssss.magicapi.core.config.Constants;
import org.ssssssss.magicapi.core.web.MagicController;
import org.ssssssss.magicapi.core.web.MagicExceptionHandler;
import org.ssssssss.magicapi.core.config.MagicConfiguration;
import org.ssssssss.magicapi.core.model.*;
import org.ssssssss.magicapi.backup.service.MagicBackupService;
import org.ssssssss.magicapi.core.service.MagicDynamicRegistry;
import org.ssssssss.magicapi.utils.JsonUtils;
import org.ssssssss.magicapi.utils.WebUtils;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
public class MagicBackupController extends MagicController implements MagicExceptionHandler {
private final MagicBackupService service;
public MagicBackupController(MagicConfiguration configuration) {
super(configuration);
this.service = configuration.getMagicBackupService();
}
@GetMapping("/backups")
@ResponseBody
public JsonBean<List<Backup>> backups(Long timestamp) {
if(service == null){
return new JsonBean<>(Collections.emptyList());
}
return new JsonBean<>(service.backupList(timestamp == null ? System.currentTimeMillis() : timestamp));
}
@GetMapping("/backup/{id}")
@ResponseBody
public JsonBean<List<Backup>> backups(@PathVariable("id") String id) {
if(service == null || StringUtils.isBlank(id)){
return new JsonBean<>(Collections.emptyList());
}
return new JsonBean<>(service.backupById(id));
}
@GetMapping("/backup/rollback")
@ResponseBody
public JsonBean<Boolean> rollback(String id, Long timestamp) throws IOException {
notNull(service, BACKUP_NOT_ENABLED);
Backup backup = service.backupInfo(id, timestamp);
if("full".equals(id)){
service.doBackupAll("还原全量备份前,系统自动全量备份", WebUtils.currentUserName());
configuration.getMagicAPIService().upload(new ByteArrayInputStream(backup.getContent()), Constants.UPLOAD_MODE_FULL);
return new JsonBean<>(true);
}
if(backup.getType().endsWith("-group")){
Group group = JsonUtils.readValue(backup.getContent(), Group.class);
return new JsonBean<>(MagicConfiguration.getMagicResourceService().saveGroup(group));
}
MagicEntity entity = configuration.getMagicDynamicRegistries().stream()
.map(MagicDynamicRegistry::getMagicResourceStorage)
.filter(it -> it.folder().equals(backup.getType()))
.map(it -> it.read(backup.getContent()))
.findFirst()
.orElse(null);
if(entity != null){
return new JsonBean<>(MagicConfiguration.getMagicResourceService().saveFile(entity));
}
return new JsonBean<>(false);
}
@GetMapping("/backup")
@ResponseBody
public JsonBean<String> backup(Long timestamp, String id) {
notNull(service, BACKUP_NOT_ENABLED);
notBlank(id, PARAMETER_INVALID);
notNull(timestamp, PARAMETER_INVALID);
Backup backup = service.backupInfo(id, timestamp);
MagicEntity entity = JsonUtils.readValue(backup.getContent(), MagicEntity.class);
return new JsonBean<>(entity == null ? null : entity.getScript());
}
@PostMapping("/backup/full")
@ResponseBody
public JsonBean<Boolean> doBackup() throws IOException {
notNull(service, BACKUP_NOT_ENABLED);
service.doBackupAll("主动全量备份", WebUtils.currentUserName());
return new JsonBean<>(true);
}
}
package org.ssssssss.magicapi.core.annotation;
import java.lang.annotation.*;
/**
* 模块,主要用于import指令,import时根据模块名获取当前类如:<code>import assert</code>;
*
* @author mxd
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MagicModule {
/**
* 模块名
*/
String value();
}
package org.ssssssss.magicapi.core.annotation;
import org.ssssssss.magicapi.core.config.MessageType;
import java.lang.annotation.*;
/**
* WebSocket 消息
*
* @author mxd
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Message {
/**
* @return 消息类型
*/
MessageType value();
}
package org.ssssssss.magicapi.core.annotation;
import org.ssssssss.magicapi.core.interceptor.Authorization;
import java.lang.annotation.*;
/**
* 接口验证信息
*
* @author mxd
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Valid {
/**
* 验证是否有该权限
*/
Authorization authorization() default Authorization.NONE;
/**
* 验证是否是只读模式
*/
boolean readonly() default true;
/**
* 验证是否需要登录
*/
boolean requireLogin() default true;
}
package org.ssssssss.magicapi.core.config;
/**
* 备份配置
*
* @author mxd
* @since 2.0.0
*/
public class Backup {
/**
* 是否启用备份配置,默认不启用
*/
private boolean enable = false;
/**
* 保留天数,<=0 为不限制
*/
private int maxHistory = -1;
/**
* 使用数据库存储时的表名
*/
private String tableName;
/**
* 使用数据库存储时使用的数据源
*/
private String datasource;
public int getMaxHistory() {
return maxHistory;
}
public void setMaxHistory(int maxHistory) {
this.maxHistory = maxHistory;
}
public String getTableName() {
return tableName;
}
public void setTableName(String tableName) {
this.tableName = tableName;
}
public String getDatasource() {
return datasource;
}
public void setDatasource(String datasource) {
this.datasource = datasource;
}
public boolean isEnable() {
return enable;
}
public void setEnable(boolean enable) {
this.enable = enable;
}
}
package org.ssssssss.magicapi.core.config;
/**
* 缓存配置
*
* @author mxd
*/
public class Cache {
/**
* 是否启用缓存
*/
private boolean enable = false;
/**
* 默认缓存容量
*/
private int capacity = 10000;
/**
* 默认过期时间,单位为毫秒,-1为不过期
*/
private long ttl = -1;
public int getCapacity() {
return capacity;
}
public void setCapacity(int capacity) {
this.capacity = capacity;
}
public long getTtl() {
return ttl;
}
public void setTtl(long ttl) {
this.ttl = ttl;
}
public boolean isEnable() {
return enable;
}
public void setEnable(boolean enable) {
this.enable = enable;
}
}
package org.ssssssss.magicapi.core.config;
import java.util.Arrays;
import java.util.List;
public class Constants {
/**
* true 常量
*/
public static final String CONST_STRING_TRUE = "true";
/**
* 空值
*/
public static final String EMPTY = "";
/**
* 表达式验证
*/
public static final String VALIDATE_TYPE_EXPRESSION = "expression";
/**
* 正则验证
*/
public static final String VALIDATE_TYPE_PATTERN = "pattern";
/**
* 表达式验证中变量的默认名称
*/
public static final String EXPRESSION_DEFAULT_VAR_NAME = "value";
/**
* 脚本中session的变量名
*/
public static final String VAR_NAME_SESSION = "session";
/**
* 脚本中cookie的变量名
*/
public static final String VAR_NAME_COOKIE = "cookie";
/**
* 脚本中路径变量的变量名
*/
public static final String VAR_NAME_PATH_VARIABLE = "path";
/**
* 脚本中header的变量名
*/
public static final String VAR_NAME_HEADER = "header";
/**
* 脚本中query的变量名
*/
public static final String VAR_NAME_QUERY = "query";
/**
/**
* 脚本中RequestBody的变量名
*/
public static final String VAR_NAME_REQUEST_BODY = "body";
/**
* 脚本中RequestBody的变量值字段类型
*/
public static final String VAR_NAME_REQUEST_BODY_VALUE_TYPE_OBJECT = "object";
/**
* 脚本中RequestBody的变量名字段类型
*/
public static final String VAR_NAME_REQUEST_BODY_VALUE_TYPE_ARRAY = "array";
public static final String HEADER_REQUEST_SCRIPT_ID = "Magic-Request-Script-Id";
public static final String HEADER_REQUEST_CLIENT_ID = "Magic-Request-Client-Id";
public static final String HEADER_REQUEST_BREAKPOINTS = "Magic-Request-Breakpoints";
public static final String ATTRIBUTE_MAGIC_USER = "MAGIC_API_ATTRIBUTE_USER";
public static final String MAGIC_TOKEN_HEADER = "Magic-Token";
public static final String GROUP_METABASE = "group.json";
public static final String UPLOAD_MODE_FULL = "full";
public static final String LOCK = "1";
public static final String UNLOCK = "0";
public static final String ROOT_ID = "0";
public static final String EVENT_TYPE_FILE = "file";
public static final String EVENT_SOURCE_NOTIFY = "notify";
public static final String WEBSOCKET_ATTRIBUTE_FILE_ID = "fileId";
public static final String WEBSOCKET_ATTRIBUTE_USER_ID = "id";
public static final String WEBSOCKET_ATTRIBUTE_USER_NAME = "username";
public static final String WEBSOCKET_ATTRIBUTE_USER_IP = "ip";
public static final String WEBSOCKET_ATTRIBUTE_CLIENT_ID = "cid";
/**
* 执行成功的code值
*/
public static int RESPONSE_CODE_SUCCESS = 1;
/**
* 执行成功的message值
*/
public static final String RESPONSE_MESSAGE_SUCCESS = "success";
/**
* 执行出现异常的code值
*/
public static int RESPONSE_CODE_EXCEPTION = -1;
/**
* 参数验证未通过的code值
*/
public static int RESPONSE_CODE_INVALID = 0;
/**
* 空数组
*/
public static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
/**
* The Es driver.
*/
public static final String ES_DRIVER = "org.elasticsearch.xpack.sql.jdbc.EsDriver";
}
package org.ssssssss.magicapi.core.config;
/**
* CRUD 配置
*
* @author 冰点
* @date 2021-7-15 09:26:17
* @since 1.3.4
*/
public class Crud {
/**
* 逻辑删除列
*/
private String logicDeleteColumn = "is_valid";
/**
* 逻辑删除值
*/
private String logicDeleteValue = "0";
public String getLogicDeleteColumn() {
return logicDeleteColumn;
}
public void setLogicDeleteColumn(String logicDeleteColumn) {
this.logicDeleteColumn = logicDeleteColumn;
}
public String getLogicDeleteValue() {
return logicDeleteValue;
}
public void setLogicDeleteValue(String logicDeleteValue) {
this.logicDeleteValue = logicDeleteValue;
}
}
package org.ssssssss.magicapi.core.config;
/**
* Debug配置
*
* @author mxd
*/
public class Debug {
/**
* 断点超时时间
*/
private int timeout = 60;
public int getTimeout() {
return timeout;
}
public void setTimeout(int timeout) {
this.timeout = timeout;
}
}
package org.ssssssss.magicapi.core.config;
import org.apache.commons.lang3.StringUtils;
import org.ssssssss.magicapi.core.exception.InvalidArgumentException;
import org.ssssssss.magicapi.core.model.JsonCode;
public interface JsonCodeConstants {
JsonCode SUCCESS = new JsonCode(1, Constants.RESPONSE_MESSAGE_SUCCESS);
JsonCode IS_READ_ONLY = new JsonCode(-2, "当前为只读模式,无法操作");
JsonCode PERMISSION_INVALID = new JsonCode(-10, "无权限操作.");
JsonCode GROUP_NOT_FOUND = new JsonCode(1001, "找不到分组信息");
JsonCode NOT_SUPPORTED_GROUP_TYPE = new JsonCode(1002, "不支持该分组类型");
JsonCode TARGET_IS_REQUIRED = new JsonCode(1003, "目标网址不能为空");
JsonCode SECRET_KEY_IS_REQUIRED = new JsonCode(1004, "secretKey不能为空");
JsonCode MOVE_NAME_CONFLICT = new JsonCode(1005, "移动后名称会重复,请修改名称后在试。");
JsonCode SRC_GROUP_CONFLICT = new JsonCode(1006, "源对象和分组不能一致");
JsonCode FILE_NOT_FOUND = new JsonCode(1007, "找不到对应文件或分组");
JsonCode RESOURCE_LOCKED = new JsonCode(1008, "当前资源已被锁定,请解锁后在操作。");
JsonCode PATH_CONFLICT = new JsonCode(1009, "该路径已被使用,请换一个路径在试");
JsonCode RESOURCE_PATH_CONFLICT = new JsonCode(1010, "资源中[%s]有冲突,请检查");
JsonCode MOVE_PATH_CONFLICT = new JsonCode(1011, "移动后路径会冲突,请换一个路径在试");
JsonCode SAVE_GROUP_PATH_CONFLICT = new JsonCode(1036, "保存后路径会冲突,请换一个路径在试");
JsonCode REQUEST_METHOD_REQUIRED = new JsonCode(1012, "请求方法不能为空");
JsonCode REQUEST_PATH_REQUIRED = new JsonCode(1013, "请求路径不能为空");
JsonCode FUNCTION_PATH_REQUIRED = new JsonCode(1014, "函数路径不能为空");
JsonCode FILE_PATH_NOT_EXISTS = new JsonCode(1015, "配置的文件路径不存在,请检查");
JsonCode REQUEST_PATH_CONFLICT = new JsonCode(1016, "接口[%s(%s)]与应用冲突,无法注册");
JsonCode SCRIPT_REQUIRED = new JsonCode(1017, "脚本内容不能为空");
JsonCode NAME_REQUIRED = new JsonCode(1018, "名称不能为空");
JsonCode PATH_REQUIRED = new JsonCode(1019, "路径不能为空");
JsonCode DS_URL_REQUIRED = new JsonCode(1020, "jdbcURL不能为空");
JsonCode DS_KEY_REQUIRED = new JsonCode(1021, "key不能为空");
JsonCode DS_KEY_CONFLICT = new JsonCode(1022, "数据源key已被使用,请更换后在试");
JsonCode GROUP_ID_REQUIRED = new JsonCode(1023, "请选择分组");
JsonCode CRON_ID_REQUIRED = new JsonCode(1024, "cron表达式不能为空");
JsonCode NAME_INVALID = new JsonCode(1025, "名称不能包含特殊字符,只允许中文、数字、字母以及+_-.()的组合且不能.开头");
JsonCode DATASOURCE_KEY_INVALID = new JsonCode(1026, "数据源Key不能包含特殊字符,只允许中文、数字、字母以及_组合");
JsonCode FILE_SAVE_FAILURE = new JsonCode(1027, "保存失败,同一组下分组名称不能重复且不能包含特殊字符。");
JsonCode PARAMETER_INVALID = new JsonCode(1028, "参数验证失败");
JsonCode HEADER_INVALID = new JsonCode(1029, "header验证失败");
JsonCode PATH_VARIABLE_INVALID = new JsonCode(1030, "路径变量验证失败");
JsonCode BODY_INVALID = new JsonCode(1031, "body验证失败");
JsonCode FILE_IS_REQUIRED = new JsonCode(1032, "请上传文件");
JsonCode SIGN_IS_INVALID = new JsonCode(1033, "签名验证失败,请检查秘钥是否正确");
JsonCode BACKUP_NOT_ENABLED = new JsonCode(1034, "未启用备份,无法操作");
JsonCode API_NOT_FOUND = new JsonCode(1035, "找不到接口");
default void notNull(Object value, JsonCode jsonCode) {
if (value == null) {
throw new InvalidArgumentException(jsonCode);
}
}
default void isTrue(boolean value, JsonCode jsonCode) {
if (!value) {
throw new InvalidArgumentException(jsonCode);
}
}
default void notBlank(String value, JsonCode jsonCode) {
isTrue(StringUtils.isNotBlank(value), jsonCode);
}
}
package org.ssssssss.magicapi.core.config;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.ssssssss.magicapi.core.web.RequestHandler;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
/**
* magic-api配置信息
*
* @author mxd
*/
@ConfigurationProperties(prefix = "magic-api")
public class MagicAPIProperties {
/**
* 版本号
*/
private final String version = RequestHandler.class.getPackage().getImplementationVersion();
/**
* web页面入口
*/
private String web;
/**
* 接口路径前缀
*/
private String prefix;
/**
* 打印banner
*/
private boolean banner = true;
/**
* 是否抛出异常
*/
private boolean throwException = false;
/**
* 自动导入的模块,多个用","分隔
*
* @since 0.3.2
*/
private String autoImportModule = "db";
/**
* 可自动导入的包(目前只支持以.*结尾的通配符),多个用","分隔
*
* @since 0.4.0
*/
private String autoImportPackage;
/**
* 是否允许覆盖应用接口,默认为false
*
* @since 0.4.0
*/
private boolean allowOverride = false;
/**
* SQL列名转换
*
* @since 0.5.0
*/
private String sqlColumnCase = "default";
/**
* 线程核心数,需要>0,<=0时采用默认配置,即CPU核心数 * 2
*
* @since 0.4.5
*/
private int threadPoolExecutorSize = 0;
/**
* 编辑器配置文件路径(js)
*
* @since 0.6.1
*/
private String editorConfig;
/**
* 是否启用跨域支持
*
* @since 1.0.0
*/
private boolean supportCrossDomain = true;
/**
* JSON响应结构表达式
*
* @since 1.0.0
*/
private String response;
/**
* 远程推送时的秘钥,未配置则不开启
*
* @since 1.2.1
*/
private String secretKey;
/**
* 远程推送的路径,默认为/_magic-api-sync
*
* @since 1.2.1
*/
private String pushPath = "/_magic-api-sync";
/**
* 启动完成之后打印地址
*/
private boolean showUrl = true;
/**
* 是否要打印SQL
*
* @since 1.5.0
*/
private boolean showSql = true;
/**
* 请求参数日期格式
*
* @since 1.5.2
*/
private String[] datePattern = new String[]{"yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyyMMddHHmmss", "yyyyMMdd"};
/**
* 编译缓存容量
*
* @since 1.5.3
*/
private int compileCacheSize = 500;
/**
* 是否持久化保存ResponseBody
*
* @since 1.5.3
*/
private boolean persistenceResponseBody = true;
/**
* 实例ID,集群环境下,要保证每台机器不同。默认启动后随机生成uuid
*/
private String instanceId = UUID.randomUUID().toString();
@NestedConfigurationProperty
private Security security = new Security();
@NestedConfigurationProperty
private Page page = new Page();
@NestedConfigurationProperty
private Cache cache = new Cache();
@NestedConfigurationProperty
private Debug debug = new Debug();
@NestedConfigurationProperty
private Resource resource = new Resource();
@NestedConfigurationProperty
private ResponseCode responseCode = new ResponseCode();
@NestedConfigurationProperty
private Crud crud = new Crud();
@NestedConfigurationProperty
private Backup backup = new Backup();
public String getWeb() {
if (StringUtils.isBlank(web)) {
return null;
}
if (web.endsWith("/**")) {
return web.substring(0, web.length() - 3);
}
if (web.endsWith("/*")) {
return web.substring(0, web.length() - 2);
}
if (web.endsWith("/")) {
return web.substring(0, web.length() - 1);
}
return web;
}
public void setWeb(String web) {
this.web = web;
}
public String getSqlColumnCase() {
return sqlColumnCase;
}
public void setSqlColumnCase(String sqlColumnCase) {
this.sqlColumnCase = sqlColumnCase;
}
public boolean isBanner() {
return banner;
}
public void setBanner(boolean banner) {
this.banner = banner;
}
public List<String> getAutoImportModuleList() {
return Arrays.asList(autoImportModule.replaceAll("\\s", "").split(","));
}
public List<String> getAutoImportPackageList() {
if (autoImportPackage == null) {
return Collections.emptyList();
}
return Arrays.asList(autoImportPackage.replaceAll("\\s", "").split(","));
}
public String getVersion() {
return version;
}
public String getPrefix() {
return prefix;
}
public void setPrefix(String prefix) {
this.prefix = prefix;
}
public boolean isThrowException() {
return throwException;
}
public void setThrowException(boolean throwException) {
this.throwException = throwException;
}
public String getAutoImportModule() {
return autoImportModule;
}
public void setAutoImportModule(String autoImportModule) {
this.autoImportModule = autoImportModule;
}
public String getAutoImportPackage() {
return autoImportPackage;
}
public void setAutoImportPackage(String autoImportPackage) {
this.autoImportPackage = autoImportPackage;
}
public boolean isAllowOverride() {
return allowOverride;
}
public void setAllowOverride(boolean allowOverride) {
this.allowOverride = allowOverride;
}
public int getThreadPoolExecutorSize() {
return threadPoolExecutorSize;
}
public void setThreadPoolExecutorSize(int threadPoolExecutorSize) {
this.threadPoolExecutorSize = threadPoolExecutorSize;
}
public String getEditorConfig() {
return editorConfig;
}
public void setEditorConfig(String editorConfig) {
this.editorConfig = editorConfig;
}
public boolean isSupportCrossDomain() {
return supportCrossDomain;
}
public void setSupportCrossDomain(boolean supportCrossDomain) {
this.supportCrossDomain = supportCrossDomain;
}
public String getResponse() {
return response;
}
public void setResponse(String response) {
this.response = response;
}
public String getSecretKey() {
return secretKey;
}
public void setSecretKey(String secretKey) {
this.secretKey = secretKey;
}
public String getPushPath() {
return pushPath;
}
public void setPushPath(String pushPath) {
this.pushPath = pushPath;
}
public boolean isShowUrl() {
return showUrl;
}
public void setShowUrl(boolean showUrl) {
this.showUrl = showUrl;
}
public boolean isShowSql() {
return showSql;
}
public void setShowSql(boolean showSql) {
this.showSql = showSql;
}
public String[] getDatePattern() {
return datePattern;
}
public void setDatePattern(String[] datePattern) {
this.datePattern = datePattern;
}
public int getCompileCacheSize() {
return compileCacheSize;
}
public void setCompileCacheSize(int compileCacheSize) {
this.compileCacheSize = compileCacheSize;
}
public boolean isPersistenceResponseBody() {
return persistenceResponseBody;
}
public void setPersistenceResponseBody(boolean persistenceResponseBody) {
this.persistenceResponseBody = persistenceResponseBody;
}
public String getInstanceId() {
return instanceId;
}
public void setInstanceId(String instanceId) {
this.instanceId = instanceId;
}
public Security getSecurity() {
return security;
}
public void setSecurity(Security security) {
this.security = security;
}
public Page getPage() {
return page;
}
public void setPage(Page page) {
this.page = page;
}
public Cache getCache() {
return cache;
}
public void setCache(Cache cache) {
this.cache = cache;
}
public Debug getDebug() {
return debug;
}
public void setDebug(Debug debug) {
this.debug = debug;
}
public Resource getResource() {
return resource;
}
public void setResource(Resource resource) {
this.resource = resource;
}
public ResponseCode getResponseCode() {
return responseCode;
}
public void setResponseCode(ResponseCode responseCode) {
this.responseCode = responseCode;
}
public Crud getCrud() {
return crud;
}
public void setCrud(Crud crud) {
this.crud = crud;
}
public Backup getBackup() {
return backup;
}
public void setBackup(Backup backup) {
this.backup = backup;
}
}
package org.ssssssss.magicapi.core.config;
import org.springframework.http.converter.HttpMessageConverter;
import org.ssssssss.magicapi.core.resource.Resource;
import org.ssssssss.magicapi.core.web.RequestHandler;
import org.ssssssss.magicapi.core.model.MagicEntity;
import org.ssssssss.magicapi.core.service.MagicDynamicRegistry;
import org.ssssssss.magicapi.core.service.MagicResourceService;
import org.ssssssss.magicapi.core.interceptor.AuthorizationInterceptor;
import org.ssssssss.magicapi.core.interceptor.RequestInterceptor;
import org.ssssssss.magicapi.core.service.MagicAPIService;
import org.ssssssss.magicapi.backup.service.MagicBackupService;
import org.ssssssss.magicapi.core.service.MagicNotifyService;
import org.ssssssss.magicapi.core.interceptor.ResultProvider;
import org.ssssssss.magicapi.datasource.model.MagicDynamicDataSource;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
public class MagicConfiguration {
/**
* 拦截器
*/
private final List<RequestInterceptor> requestInterceptors = new ArrayList<>();
/**
* 编辑器配置文件
*/
private String editorConfig;
private MagicAPIService magicAPIService;
private MagicDynamicDataSource magicDynamicDataSource;
/**
* 请求出错时,是否抛出异常
*/
private boolean throwException = false;
/**
* 结果处理器
*/
private ResultProvider resultProvider;
private MagicNotifyService magicNotifyService;
private String instanceId;
private Resource workspace;
private List<HttpMessageConverter<?>> httpMessageConverters = new ArrayList<>();
private AuthorizationInterceptor authorizationInterceptor;
private MagicBackupService magicBackupService;
private static MagicResourceService magicResourceService;
private List<MagicDynamicRegistry<? extends MagicEntity>> magicDynamicRegistries;
/**
* debug 超时时间
*/
private int debugTimeout;
private boolean enableWeb = false;
public void addRequestInterceptor(RequestInterceptor requestInterceptor) {
this.requestInterceptors.add(requestInterceptor);
}
public AuthorizationInterceptor getAuthorizationInterceptor() {
return authorizationInterceptor;
}
public void setAuthorizationInterceptor(AuthorizationInterceptor authorizationInterceptor) {
this.authorizationInterceptor = authorizationInterceptor;
}
public List<RequestInterceptor> getRequestInterceptors() {
return requestInterceptors;
}
public boolean isThrowException() {
return throwException;
}
public void setThrowException(boolean throwException) {
this.throwException = throwException;
}
public ResultProvider getResultProvider() {
return resultProvider;
}
public void setResultProvider(ResultProvider resultProvider) {
this.resultProvider = resultProvider;
}
public List<HttpMessageConverter<?>> getHttpMessageConverters() {
return httpMessageConverters;
}
public void setHttpMessageConverters(List<HttpMessageConverter<?>> httpMessageConverters) {
this.httpMessageConverters = httpMessageConverters;
}
public int getDebugTimeout() {
return debugTimeout;
}
public void setDebugTimeout(int debugTimeout) {
this.debugTimeout = debugTimeout;
}
public boolean isEnableWeb() {
return enableWeb;
}
public void setEnableWeb(boolean enableWeb) {
this.enableWeb = enableWeb;
}
public String getEditorConfig() {
return editorConfig;
}
public void setEditorConfig(String editorConfig) {
this.editorConfig = editorConfig;
}
public Resource getWorkspace() {
return workspace;
}
public void setWorkspace(Resource workspace) {
this.workspace = workspace;
}
public MagicAPIService getMagicAPIService() {
return magicAPIService;
}
public void setMagicAPIService(MagicAPIService magicAPIService) {
this.magicAPIService = magicAPIService;
}
public MagicNotifyService getMagicNotifyService() {
return magicNotifyService;
}
public void setMagicNotifyService(MagicNotifyService magicNotifyService) {
this.magicNotifyService = magicNotifyService;
}
public String getInstanceId() {
return instanceId;
}
public void setInstanceId(String instanceId) {
this.instanceId = instanceId;
}
public MagicBackupService getMagicBackupService() {
return magicBackupService;
}
public void setMagicBackupService(MagicBackupService magicBackupService) {
this.magicBackupService = magicBackupService;
}
public MagicDynamicDataSource getMagicDynamicDataSource() {
return magicDynamicDataSource;
}
public void setMagicDynamicDataSource(MagicDynamicDataSource magicDynamicDataSource) {
this.magicDynamicDataSource = magicDynamicDataSource;
}
public static MagicResourceService getMagicResourceService() {
return MagicConfiguration.magicResourceService;
}
public void setMagicResourceService(MagicResourceService magicResourceService) {
MagicConfiguration.magicResourceService = magicResourceService;
}
public List<MagicDynamicRegistry<? extends MagicEntity>> getMagicDynamicRegistries() {
return magicDynamicRegistries;
}
public void setMagicDynamicRegistries(List<MagicDynamicRegistry<? extends MagicEntity>> magicDynamicRegistries) {
this.magicDynamicRegistries = magicDynamicRegistries;
}
/**
* 打印banner
*/
public void printBanner(List<String> plugins) {
System.out.println(" __ __ _ _ ____ ___ ");
System.out.println(" | \\/ | __ _ __ _ (_) ___ / \\ | _ \\|_ _|");
System.out.println(" | |\\/| | / _` | / _` || | / __| / _ \\ | |_) || | ");
System.out.println(" | | | || (_| || (_| || || (__ / ___ \\ | __/ | | ");
System.out.println(" |_| |_| \\__,_| \\__, ||_| \\___|/_/ \\_\\|_| |___|");
System.out.println(" |___/ " + Objects.toString(RequestHandler.class.getPackage().getImplementationVersion(), ""));
if(!plugins.isEmpty()){
System.out.println("集成插件:");
plugins.stream().peek(it -> System.out.print("- ")).forEach(System.out::println);
}
}
}
package org.ssssssss.magicapi.core.config;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpHeaders;
import org.ssssssss.magicapi.core.servlet.MagicHttpServletRequest;
import org.ssssssss.magicapi.core.servlet.MagicHttpServletResponse;
public abstract class MagicCorsFilter {
public void process(MagicHttpServletRequest request, MagicHttpServletResponse response) {
if (StringUtils.isNotBlank(Constants.HEADER_REQUEST_CLIENT_ID)) {
String value = request.getHeader(HttpHeaders.ORIGIN);
response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, StringUtils.isBlank(value) ? "*" : value);
response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, Constants.CONST_STRING_TRUE);
value = request.getHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS);
if (StringUtils.isNotBlank(value)) {
response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, value);
}
value = request.getHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD);
response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, StringUtils.isBlank(value) ? "GET,POST,OPTIONS,PUT,DELETE" : value);
}
}
}
package org.ssssssss.magicapi.core.config;
/**
* 函数,主要用于脚本中直接可使用的函数,如 now();
*
* @author mxd
*/
public interface MagicFunction {
}
package org.ssssssss.magicapi.core.config;
import org.ssssssss.magicapi.core.model.Plugin;
import org.ssssssss.magicapi.core.web.MagicControllerRegister;
public interface MagicPluginConfiguration {
Plugin plugin();
/**
* 注册Controller
*/
default MagicControllerRegister controllerRegister(){
return (mapping, configuration) -> { };
}
}
package org.ssssssss.magicapi.core.config;
/**
* 消息类型
*/
public enum MessageType {
/* S -> C message */
/* 日志消息 */
LOG,
/* 多个日志消息 */
LOGS,
/* 进入断点 */
BREAKPOINT,
/* 请求接口发生异常 */
EXCEPTION,
/* 登录结果 */
LOGIN_RESPONSE,
/* 刷新token */
REFRESH_TOKEN,
/* 通知客户端,有用户上线 */
USER_LOGIN,
/* 通知客户端,有用户下线 */
USER_LOGOUT,
/* 通知客户端,当前机器在线人数 */
ONLINE_USERS,
/* 通知客户端,他人进入文件*/
INTO_FILE_ID,
/* ping */
PING,
/* C -> S message */
/* 设置断点 */
SET_BREAKPOINT,
/* 恢复断点 */
RESUME_BREAKPOINT,
/* 登录 */
LOGIN,
/* 设置当前所在文件 */
SET_FILE_ID,
/* PONG */
PONG,
/* S <-> S -> C message*/
/* 获取当前在线用户 */
SEND_ONLINE
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment